| 1 | efrain | 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/>.
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 18 |   | 
        
           |  |  | 19 |   | 
        
           |  |  | 20 | // Quiz.
 | 
        
           |  |  | 21 | require_once($CFG->dirroot . "/mod/quiz/renderer.php");
 | 
        
           | 710 | ariadna | 22 | class theme_universe_mod_quiz_renderer extends mod_quiz\output\renderer
 | 
        
           |  |  | 23 | {
 | 
        
           | 1 | efrain | 24 |     /**
 | 
        
           |  |  | 25 |      * Outputs a box.
 | 
        
           |  |  | 26 |      *
 | 
        
           |  |  | 27 |      * @param string $contents The contents of the box
 | 
        
           |  |  | 28 |      * @param string $classes A space-separated list of CSS classes
 | 
        
           |  |  | 29 |      * @param string $id An optional ID
 | 
        
           |  |  | 30 |      * @param array $attributes An array of other attributes to give the box.
 | 
        
           |  |  | 31 |      * @return string the HTML to output.
 | 
        
           |  |  | 32 |      */
 | 
        
           | 710 | ariadna | 33 |     public function space_box($contents, $classes = 'generalbox', $id = null, $attributes = array())
 | 
        
           |  |  | 34 |     {
 | 
        
           | 1 | efrain | 35 |         return $this->space_box_start($classes, $id, $attributes) . $contents . $this->space_box_end();
 | 
        
           |  |  | 36 |     }
 | 
        
           |  |  | 37 |   | 
        
           |  |  | 38 |     /**
 | 
        
           |  |  | 39 |      * Outputs the opening section of a box.
 | 
        
           |  |  | 40 |      *
 | 
        
           |  |  | 41 |      * @param string $classes A space-separated list of CSS classes
 | 
        
           |  |  | 42 |      * @param string $id An optional ID
 | 
        
           |  |  | 43 |      * @param array $attributes An array of other attributes to give the box.
 | 
        
           |  |  | 44 |      * @return string the HTML to output.
 | 
        
           |  |  | 45 |      */
 | 
        
           | 710 | ariadna | 46 |     public function space_box_start($classes = 'generalbox', $id = null, $attributes = array())
 | 
        
           |  |  | 47 |     {
 | 
        
           | 1 | efrain | 48 |         $this->opencontainers->push('box', html_writer::end_tag('div'));
 | 
        
           |  |  | 49 |         $attributes['id'] = $id;
 | 
        
           |  |  | 50 |         $attributes['class'] = 'box ' . renderer_base::prepare_classes($classes);
 | 
        
           |  |  | 51 |         return html_writer::start_tag('div', $attributes);
 | 
        
           |  |  | 52 |     }
 | 
        
           |  |  | 53 |   | 
        
           |  |  | 54 |     /**
 | 
        
           |  |  | 55 |      * Outputs the closing section of a box.
 | 
        
           |  |  | 56 |      *
 | 
        
           |  |  | 57 |      * @return string the HTML to output.
 | 
        
           |  |  | 58 |      */
 | 
        
           | 710 | ariadna | 59 |     public function space_box_end()
 | 
        
           |  |  | 60 |     {
 | 
        
           | 1 | efrain | 61 |         return $this->opencontainers->pop('box');
 | 
        
           |  |  | 62 |     }
 | 
        
           |  |  | 63 |   | 
        
           |  |  | 64 |     /**
 | 
        
           |  |  | 65 |      * Output the page information
 | 
        
           |  |  | 66 |      *
 | 
        
           |  |  | 67 |      * @param object $quiz the quiz settings.
 | 
        
           |  |  | 68 |      * @param object $cm the course_module object.
 | 
        
           |  |  | 69 |      * @param context $context the quiz context.
 | 
        
           |  |  | 70 |      * @param array $messages any access messages that should be described.
 | 
        
           |  |  | 71 |      * @param bool $quizhasquestions does quiz has questions added.
 | 
        
           |  |  | 72 |      * @return string HTML to output.
 | 
        
           |  |  | 73 |      */
 | 
        
           | 710 | ariadna | 74 |     public function view_information($quiz, $cm, $context, $messages, bool $quizhasquestions = false)
 | 
        
           |  |  | 75 |     {
 | 
        
           | 1 | efrain | 76 |         $output = '';
 | 
        
           |  |  | 77 |   | 
        
           |  |  | 78 |         // Output any access messages.
 | 
        
           |  |  | 79 |         if ($messages) {
 | 
        
           |  |  | 80 |             $output .= $this->box($this->access_messages($messages), 'rui-quizinfo quizinfo mt-3');
 | 
        
           |  |  | 81 |         }
 | 
        
           |  |  | 82 |   | 
        
           |  |  | 83 |         // Show number of attempts summary to those who can view reports.
 | 
        
           |  |  | 84 |         if (has_capability('mod/quiz:viewreports', $context)) {
 | 
        
           |  |  | 85 |             if (
 | 
        
           |  |  | 86 |                 $strattemptnum = $this->quiz_attempt_summary_link_to_reports(
 | 
        
           |  |  | 87 |                     $quiz,
 | 
        
           |  |  | 88 |                     $cm,
 | 
        
           |  |  | 89 |                     $context
 | 
        
           |  |  | 90 |                 )
 | 
        
           |  |  | 91 |             ) {
 | 
        
           |  |  | 92 |                 $output .= html_writer::tag(
 | 
        
           |  |  | 93 |                     'div',
 | 
        
           |  |  | 94 |                     $strattemptnum,
 | 
        
           |  |  | 95 |                     array('class' => 'rui-quizattemptcounts quizattemptcounts my-4')
 | 
        
           |  |  | 96 |                 );
 | 
        
           |  |  | 97 |             }
 | 
        
           |  |  | 98 |         }
 | 
        
           |  |  | 99 |   | 
        
           |  |  | 100 |         if (has_any_capability(['mod/quiz:manageoverrides', 'mod/quiz:viewoverrides'], $context)) {
 | 
        
           |  |  | 101 |             if ($overrideinfo = $this->quiz_override_summary_links($quiz, $cm)) {
 | 
        
           |  |  | 102 |                 $output .= html_writer::tag('div', $overrideinfo, ['class' => 'rui-quizattemptcounts quizattemptcounts my-4']);
 | 
        
           |  |  | 103 |             }
 | 
        
           |  |  | 104 |         }
 | 
        
           |  |  | 105 |   | 
        
           |  |  | 106 |         return $output;
 | 
        
           |  |  | 107 |     }
 | 
        
           |  |  | 108 |   | 
        
           |  |  | 109 |     /**
 | 
        
           |  |  | 110 |      * Generates the table of data
 | 
        
           |  |  | 111 |      *
 | 
        
           |  |  | 112 |      * @param array $quiz Array contining quiz data
 | 
        
           |  |  | 113 |      * @param int $context The page context ID
 | 
        
           |  |  | 114 |      * @param mod_quiz_view_object $viewobj
 | 
        
           |  |  | 115 |      */
 | 
        
           | 710 | ariadna | 116 |     public function view_table($quiz, $context, $viewobj)
 | 
        
           |  |  | 117 |     {
 | 
        
           | 1 | efrain | 118 |         if (!$viewobj->attempts) {
 | 
        
           |  |  | 119 |             return '';
 | 
        
           |  |  | 120 |         }
 | 
        
           |  |  | 121 |   | 
        
           |  |  | 122 |         // Prepare table header.
 | 
        
           |  |  | 123 |         $table = new html_table();
 | 
        
           |  |  | 124 |         $table->attributes['class'] = 'generaltable rui-quizattemptsummary mt-2 mb-0';
 | 
        
           |  |  | 125 |         $table->head = array();
 | 
        
           |  |  | 126 |         $table->align = array();
 | 
        
           |  |  | 127 |         $table->size = array();
 | 
        
           |  |  | 128 |         if ($viewobj->attemptcolumn) {
 | 
        
           |  |  | 129 |             $table->head[] = get_string('attemptnumber', 'quiz');
 | 
        
           |  |  | 130 |             $table->size[] = '';
 | 
        
           |  |  | 131 |         }
 | 
        
           |  |  | 132 |         $table->head[] = get_string('attemptstate', 'quiz');
 | 
        
           |  |  | 133 |         $table->align[] = 'left';
 | 
        
           |  |  | 134 |         $table->size[] = '';
 | 
        
           |  |  | 135 |         if ($viewobj->markcolumn) {
 | 
        
           |  |  | 136 |             $table->head[] = get_string('marks', 'quiz') . ' / ' .
 | 
        
           |  |  | 137 |                 quiz_format_grade($quiz, $quiz->sumgrades);
 | 
        
           |  |  | 138 |             $table->size[] = '';
 | 
        
           |  |  | 139 |         }
 | 
        
           |  |  | 140 |         if ($viewobj->gradecolumn) {
 | 
        
           |  |  | 141 |             $table->head[] = get_string('grade', 'quiz') . ' / ' .
 | 
        
           |  |  | 142 |                 quiz_format_grade($quiz, $quiz->grade);
 | 
        
           |  |  | 143 |             $table->size[] = '';
 | 
        
           |  |  | 144 |         }
 | 
        
           |  |  | 145 |         if ($viewobj->canreviewmine) {
 | 
        
           |  |  | 146 |             $table->head[] = get_string('review', 'quiz');
 | 
        
           |  |  | 147 |             $table->size[] = '';
 | 
        
           |  |  | 148 |         }
 | 
        
           |  |  | 149 |         if ($viewobj->feedbackcolumn) {
 | 
        
           |  |  | 150 |             $table->head[] = get_string('feedback', 'quiz');
 | 
        
           |  |  | 151 |             $table->align[] = 'left';
 | 
        
           |  |  | 152 |             $table->size[] = '';
 | 
        
           |  |  | 153 |         }
 | 
        
           |  |  | 154 |   | 
        
           |  |  | 155 |         // One row for each attempt.
 | 
        
           |  |  | 156 |         foreach ($viewobj->attemptobjs as $attemptobj) {
 | 
        
           |  |  | 157 |             $attemptoptions = $attemptobj->get_display_options(true);
 | 
        
           |  |  | 158 |             $row = array();
 | 
        
           |  |  | 159 |   | 
        
           |  |  | 160 |             // Add the attempt number.
 | 
        
           |  |  | 161 |             if ($viewobj->attemptcolumn) {
 | 
        
           |  |  | 162 |                 if ($attemptobj->is_preview()) {
 | 
        
           |  |  | 163 |                     $row[] = get_string('preview', 'quiz');
 | 
        
           |  |  | 164 |                 } else {
 | 
        
           |  |  | 165 |                     $row[] = $attemptobj->get_attempt_number();
 | 
        
           |  |  | 166 |                 }
 | 
        
           |  |  | 167 |             }
 | 
        
           |  |  | 168 |   | 
        
           |  |  | 169 |             $row[] = $this->attempt_state($attemptobj);
 | 
        
           |  |  | 170 |   | 
        
           |  |  | 171 |             if ($viewobj->markcolumn) {
 | 
        
           |  |  | 172 |                 if (
 | 
        
           |  |  | 173 |                     $attemptoptions->marks >= question_display_options::MARK_AND_MAX &&
 | 
        
           |  |  | 174 |                     $attemptobj->is_finished()
 | 
        
           |  |  | 175 |                 ) {
 | 
        
           |  |  | 176 |                     $row[] = quiz_format_grade($quiz, $attemptobj->get_sum_marks());
 | 
        
           |  |  | 177 |                 } else {
 | 
        
           |  |  | 178 |                     $row[] = '';
 | 
        
           |  |  | 179 |                 }
 | 
        
           |  |  | 180 |             }
 | 
        
           |  |  | 181 |   | 
        
           |  |  | 182 |             // Ouside the if because we may be showing feedback but not grades.
 | 
        
           |  |  | 183 |             $attemptgrade = quiz_rescale_grade($attemptobj->get_sum_marks(), $quiz, false);
 | 
        
           |  |  | 184 |   | 
        
           |  |  | 185 |             if ($viewobj->gradecolumn) {
 | 
        
           |  |  | 186 |                 if (
 | 
        
           |  |  | 187 |                     $attemptoptions->marks >= question_display_options::MARK_AND_MAX &&
 | 
        
           |  |  | 188 |                     $attemptobj->is_finished()
 | 
        
           |  |  | 189 |                 ) {
 | 
        
           |  |  | 190 |   | 
        
           |  |  | 191 |                     // Highlight the highest grade if appropriate.
 | 
        
           |  |  | 192 |                     if (
 | 
        
           |  |  | 193 |                         $viewobj->overallstats && !$attemptobj->is_preview()
 | 
        
           |  |  | 194 |                         && $viewobj->numattempts > 1 && !is_null($viewobj->mygrade)
 | 
        
           |  |  | 195 |                         && $attemptobj->get_state() == quiz_attempt::FINISHED
 | 
        
           |  |  | 196 |                         && $attemptgrade == $viewobj->mygrade
 | 
        
           |  |  | 197 |                         && $quiz->grademethod == QUIZ_GRADEHIGHEST
 | 
        
           |  |  | 198 |                     ) {
 | 
        
           |  |  | 199 |                         $table->rowclasses[$attemptobj->get_attempt_number()] = 'bestrow';
 | 
        
           |  |  | 200 |                     }
 | 
        
           |  |  | 201 |   | 
        
           |  |  | 202 |                     $row[] = quiz_format_grade($quiz, $attemptgrade);
 | 
        
           |  |  | 203 |                 } else {
 | 
        
           |  |  | 204 |                     $row[] = '';
 | 
        
           |  |  | 205 |                 }
 | 
        
           |  |  | 206 |             }
 | 
        
           |  |  | 207 |   | 
        
           |  |  | 208 |             if ($viewobj->canreviewmine) {
 | 
        
           |  |  | 209 |                 $row[] = $viewobj->accessmanager->make_review_link(
 | 
        
           |  |  | 210 |                     $attemptobj->get_attempt(),
 | 
        
           |  |  | 211 |                     $attemptoptions,
 | 
        
           |  |  | 212 |                     $this
 | 
        
           |  |  | 213 |                 );
 | 
        
           |  |  | 214 |             }
 | 
        
           |  |  | 215 |   | 
        
           |  |  | 216 |             if ($viewobj->feedbackcolumn && $attemptobj->is_finished()) {
 | 
        
           |  |  | 217 |                 if ($attemptoptions->overallfeedback) {
 | 
        
           |  |  | 218 |                     $row[] = quiz_feedback_for_grade($attemptgrade, $quiz, $context);
 | 
        
           |  |  | 219 |                 } else {
 | 
        
           |  |  | 220 |                     $row[] = '';
 | 
        
           |  |  | 221 |                 }
 | 
        
           |  |  | 222 |             }
 | 
        
           |  |  | 223 |   | 
        
           |  |  | 224 |             if ($attemptobj->is_preview()) {
 | 
        
           |  |  | 225 |                 $table->data['preview'] = $row;
 | 
        
           |  |  | 226 |             } else {
 | 
        
           |  |  | 227 |                 $table->data[$attemptobj->get_attempt_number()] = $row;
 | 
        
           |  |  | 228 |             }
 | 
        
           |  |  | 229 |         } // End. of loop over attempts.
 | 
        
           |  |  | 230 |   | 
        
           |  |  | 231 |         $output = '';
 | 
        
           |  |  | 232 |         $output .= $this->view_table_heading();
 | 
        
           |  |  | 233 |         $output .= html_writer::start_tag('div', array('class' => 'table-overflow mb-4'));
 | 
        
           |  |  | 234 |         $output .= html_writer::table($table);
 | 
        
           |  |  | 235 |         $output .= html_writer::end_tag('div');
 | 
        
           |  |  | 236 |         return $output;
 | 
        
           |  |  | 237 |     }
 | 
        
           |  |  | 238 |   | 
        
           |  |  | 239 |     /*
 | 
        
           |  |  | 240 |      * View Page
 | 
        
           |  |  | 241 |      */
 | 
        
           |  |  | 242 |     /**
 | 
        
           |  |  | 243 |      * Generates the view page
 | 
        
           |  |  | 244 |      *
 | 
        
           |  |  | 245 |      * @param stdClass $course the course settings row from the database.
 | 
        
           |  |  | 246 |      * @param stdClass $quiz the quiz settings row from the database.
 | 
        
           |  |  | 247 |      * @param stdClass $cm the course_module settings row from the database.
 | 
        
           |  |  | 248 |      * @param context_module $context the quiz context.
 | 
        
           |  |  | 249 |      * @param mod_quiz_view_object $viewobj
 | 
        
           |  |  | 250 |      * @return string HTML to display
 | 
        
           |  |  | 251 |      */
 | 
        
           | 710 | ariadna | 252 |     public function view_page($course, $quiz, $cm, $context, $viewobj)
 | 
        
           |  |  | 253 |     {
 | 
        
           | 1 | efrain | 254 |         $output = '';
 | 
        
           |  |  | 255 |   | 
        
           | 711 | ariadna | 256 |         $output .= $this->view_page_tertiary_nav($viewobj);
 | 
        
           | 1 | efrain | 257 |         $output .= $this->view_information($quiz, $cm, $context, $viewobj->infomessages);
 | 
        
           |  |  | 258 |         $output .= $this->view_table($quiz, $context, $viewobj);
 | 
        
           |  |  | 259 |         $output .= $this->view_result_info($quiz, $context, $cm, $viewobj);
 | 
        
           |  |  | 260 |         $output .= $this->box($this->view_page_buttons($viewobj), 'rui-quizattempt');
 | 
        
           |  |  | 261 |         return $output;
 | 
        
           |  |  | 262 |     }
 | 
        
           |  |  | 263 |   | 
        
           |  |  | 264 |     /**
 | 
        
           |  |  | 265 |      * Outputs the table containing data from summary data array
 | 
        
           |  |  | 266 |      *
 | 
        
           |  |  | 267 |      * @param array $summarydata contains row data for table
 | 
        
           |  |  | 268 |      * @param int $page contains the current page number
 | 
        
           |  |  | 269 |      */
 | 
        
           | 710 | ariadna | 270 |     public function review_summary_table($summarydata, $page)
 | 
        
           |  |  | 271 |     {
 | 
        
           | 1 | efrain | 272 |         $summarydata = $this->filter_review_summary_table($summarydata, $page);
 | 
        
           |  |  | 273 |         if (empty($summarydata)) {
 | 
        
           |  |  | 274 |             return '';
 | 
        
           |  |  | 275 |         }
 | 
        
           |  |  | 276 |   | 
        
           |  |  | 277 |         $output = '';
 | 
        
           |  |  | 278 |   | 
        
           |  |  | 279 |         $output .= html_writer::start_tag('div', array('class' => 'rui-summary-table'));
 | 
        
           |  |  | 280 |   | 
        
           |  |  | 281 |         $output .= html_writer::start_tag('div', array('class' => 'rui-info-container rui-quizreviewsummary'));
 | 
        
           |  |  | 282 |   | 
        
           |  |  | 283 |         foreach ($summarydata as $rowdata => $val) {
 | 
        
           |  |  | 284 |   | 
        
           |  |  | 285 |             $csstitle = $rowdata;
 | 
        
           |  |  | 286 |   | 
        
           |  |  | 287 |             if ($val['title'] instanceof renderable) {
 | 
        
           |  |  | 288 |                 $title = $this->render($val['title']);
 | 
        
           |  |  | 289 |             } else {
 | 
        
           |  |  | 290 |                 $title = $val['title'];
 | 
        
           |  |  | 291 |             }
 | 
        
           |  |  | 292 |   | 
        
           |  |  | 293 |             if ($val['content'] instanceof renderable) {
 | 
        
           |  |  | 294 |                 $content = $this->render($val['content']);
 | 
        
           |  |  | 295 |             } else {
 | 
        
           |  |  | 296 |                 $content = $val['content'];
 | 
        
           |  |  | 297 |             }
 | 
        
           |  |  | 298 |   | 
        
           |  |  | 299 |             if ($val['title'] instanceof renderable) {
 | 
        
           |  |  | 300 |                 $output .= html_writer::tag(
 | 
        
           |  |  | 301 |                     'div',
 | 
        
           |  |  | 302 |                     html_writer::tag('h5', $title, array('class' => 'rui-infobox-title')) .
 | 
        
           |  |  | 303 |                         html_writer::tag('div', $content, array('class' => 'rui-infobox-content--small')),
 | 
        
           |  |  | 304 |                     array('class' => 'rui-infobox rui-infobox--avatar')
 | 
        
           |  |  | 305 |                 );
 | 
        
           |  |  | 306 |             } else {
 | 
        
           |  |  | 307 |                 $output .= html_writer::tag(
 | 
        
           |  |  | 308 |                     'div',
 | 
        
           |  |  | 309 |                     html_writer::tag('h5', $title, array('class' => 'rui-infobox-title')) .
 | 
        
           |  |  | 310 |                         html_writer::tag('div', $content, array('class' => 'rui-infobox-content--small')),
 | 
        
           |  |  | 311 |                     array('class' => 'rui-infobox rui-infobox--' . strtolower(str_replace(' ', '', $csstitle)))
 | 
        
           |  |  | 312 |                 );
 | 
        
           |  |  | 313 |             }
 | 
        
           |  |  | 314 |         }
 | 
        
           |  |  | 315 |   | 
        
           |  |  | 316 |         $output .= html_writer::end_tag('div');
 | 
        
           |  |  | 317 |         $output .= html_writer::end_tag('div');
 | 
        
           |  |  | 318 |   | 
        
           |  |  | 319 |         return $output;
 | 
        
           |  |  | 320 |     }
 | 
        
           |  |  | 321 |   | 
        
           |  |  | 322 |     /**
 | 
        
           |  |  | 323 |      * Generates the table of summarydata
 | 
        
           |  |  | 324 |      *
 | 
        
           |  |  | 325 |      * @param quiz_attempt $attemptobj
 | 
        
           |  |  | 326 |      * @param mod_quiz_display_options $displayoptions
 | 
        
           |  |  | 327 |      */
 | 
        
           | 710 | ariadna | 328 |     public function summary_table($attemptobj, $displayoptions)
 | 
        
           |  |  | 329 |     {
 | 
        
           | 1 | efrain | 330 |         // Prepare the summary table header.
 | 
        
           |  |  | 331 |         $table = new html_table();
 | 
        
           |  |  | 332 |         $table->attributes['class'] = 'generaltable quizsummaryofattempt';
 | 
        
           |  |  | 333 |         $table->head = array(get_string('question', 'quiz'), get_string('status', 'quiz'));
 | 
        
           |  |  | 334 |         $table->align = array('left', 'left');
 | 
        
           |  |  | 335 |         $table->size = array('', '');
 | 
        
           |  |  | 336 |         $markscolumn = $displayoptions->marks >= question_display_options::MARK_AND_MAX;
 | 
        
           |  |  | 337 |         if ($markscolumn) {
 | 
        
           |  |  | 338 |             $table->head[] = get_string('marks', 'quiz');
 | 
        
           |  |  | 339 |             $table->align[] = 'left';
 | 
        
           |  |  | 340 |             $table->size[] = '';
 | 
        
           |  |  | 341 |         }
 | 
        
           |  |  | 342 |         $tablewidth = count($table->align);
 | 
        
           |  |  | 343 |         $table->data = array();
 | 
        
           |  |  | 344 |   | 
        
           |  |  | 345 |         // Get the summary info for each question.
 | 
        
           |  |  | 346 |         $slots = $attemptobj->get_slots();
 | 
        
           |  |  | 347 |         foreach ($slots as $slot) {
 | 
        
           |  |  | 348 |             // Add a section headings if we need one here.
 | 
        
           |  |  | 349 |             $heading = $attemptobj->get_heading_before_slot($slot);
 | 
        
           |  |  | 350 |   | 
        
           |  |  | 351 |             if ($heading !== null) {
 | 
        
           |  |  | 352 |                 // There is a heading here.
 | 
        
           |  |  | 353 |                 $rowclasses = 'quizsummaryheading';
 | 
        
           |  |  | 354 |                 if ($heading) {
 | 
        
           |  |  | 355 |                     $heading = format_string($heading);
 | 
        
           |  |  | 356 |                 } else if (count($attemptobj->get_quizobj()->get_sections()) > 1) {
 | 
        
           |  |  | 357 |                     // If this is the start of an unnamed section, and the quiz has more
 | 
        
           |  |  | 358 |                     // than one section, then add a default heading.
 | 
        
           |  |  | 359 |                     $heading = get_string('sectionnoname', 'quiz');
 | 
        
           |  |  | 360 |                     $rowclasses .= ' dimmed_text';
 | 
        
           |  |  | 361 |                 }
 | 
        
           |  |  | 362 |                 $cell = new html_table_cell(format_string($heading));
 | 
        
           |  |  | 363 |                 $cell->header = true;
 | 
        
           |  |  | 364 |                 $cell->colspan = $tablewidth;
 | 
        
           |  |  | 365 |                 $table->data[] = array($cell);
 | 
        
           |  |  | 366 |                 $table->rowclasses[] = $rowclasses;
 | 
        
           |  |  | 367 |             }
 | 
        
           |  |  | 368 |   | 
        
           |  |  | 369 |             // Don't display information items.
 | 
        
           |  |  | 370 |             if (!$attemptobj->is_real_question($slot)) {
 | 
        
           |  |  | 371 |                 continue;
 | 
        
           |  |  | 372 |             }
 | 
        
           |  |  | 373 |   | 
        
           |  |  | 374 |             $flag = '';
 | 
        
           |  |  | 375 |   | 
        
           |  |  | 376 |             // Real question, show it.
 | 
        
           |  |  | 377 |             if ($attemptobj->is_question_flagged($slot)) {
 | 
        
           |  |  | 378 |                 // Quiz has custom JS manipulating these image tags - so we can't use the pix_icon method here.
 | 
        
           |  |  | 379 |                 $flag = '<svg class="ml-2"
 | 
        
           |  |  | 380 |                     width="20"
 | 
        
           |  |  | 381 |                     height="20"
 | 
        
           |  |  | 382 |                     viewBox="0 0 24 24"
 | 
        
           |  |  | 383 |                     fill="none"
 | 
        
           |  |  | 384 |                     xmlns="http://www.w3.org/2000/svg"
 | 
        
           |  |  | 385 |                 >
 | 
        
           |  |  | 386 |                 <path d="M4.75 5.75V19.25"
 | 
        
           |  |  | 387 |                     stroke="currentColor"
 | 
        
           |  |  | 388 |                     stroke-width="2"
 | 
        
           |  |  | 389 |                     stroke-linecap="round"
 | 
        
           |  |  | 390 |                     stroke-linejoin="round">
 | 
        
           |  |  | 391 |                 </path>
 | 
        
           |  |  | 392 |                 <path d="M4.75 15.25V5.75C4.75 5.75 6 4.75 9 4.75C12 4.75 13.5 6.25 16 6.25C18.5
 | 
        
           |  |  | 393 |                 6.25 19.25 5.75 19.25 5.75L15.75 10.5L19.25 15.25C19.25 15.25 18.5 16.25 16
 | 
        
           |  |  | 394 |                 16.25C13.5 16.25 11.5 14.25 9 14.25C6.5 14.25 4.75 15.25 4.75 15.25Z"
 | 
        
           |  |  | 395 |                     stroke="currentColor"
 | 
        
           |  |  | 396 |                     stroke-width="2"
 | 
        
           |  |  | 397 |                     stroke-linecap="round"
 | 
        
           |  |  | 398 |                     stroke-linejoin="round"></path>
 | 
        
           |  |  | 399 |                 </svg>';
 | 
        
           |  |  | 400 |             }
 | 
        
           |  |  | 401 |             if ($attemptobj->can_navigate_to($slot)) {
 | 
        
           |  |  | 402 |                 $row = array(
 | 
        
           |  |  | 403 |                     html_writer::link(
 | 
        
           |  |  | 404 |                         $attemptobj->attempt_url($slot),
 | 
        
           |  |  | 405 |                         $attemptobj->get_question_number($slot) . $flag
 | 
        
           |  |  | 406 |                     ),
 | 
        
           |  |  | 407 |                     $attemptobj->get_question_status($slot, $displayoptions->correctness)
 | 
        
           |  |  | 408 |                 );
 | 
        
           |  |  | 409 |             } else {
 | 
        
           |  |  | 410 |                 $row = array(
 | 
        
           |  |  | 411 |                     $attemptobj->get_question_number($slot) . $flag,
 | 
        
           |  |  | 412 |                     $attemptobj->get_question_status($slot, $displayoptions->correctness)
 | 
        
           |  |  | 413 |                 );
 | 
        
           |  |  | 414 |             }
 | 
        
           |  |  | 415 |             if ($markscolumn) {
 | 
        
           |  |  | 416 |                 $row[] = $attemptobj->get_question_mark($slot);
 | 
        
           |  |  | 417 |             }
 | 
        
           |  |  | 418 |             $table->data[] = $row;
 | 
        
           |  |  | 419 |             $table->rowclasses[] = 'quizsummary' . $slot . ' ' . $attemptobj->get_question_state_class(
 | 
        
           |  |  | 420 |                 $slot,
 | 
        
           |  |  | 421 |                 $displayoptions->correctness
 | 
        
           |  |  | 422 |             );
 | 
        
           |  |  | 423 |         }
 | 
        
           |  |  | 424 |   | 
        
           |  |  | 425 |         // Print the summary table.
 | 
        
           |  |  | 426 |         $output = html_writer::table($table);
 | 
        
           |  |  | 427 |   | 
        
           |  |  | 428 |         return $output;
 | 
        
           |  |  | 429 |     }
 | 
        
           |  |  | 430 | }
 | 
        
           |  |  | 431 |   | 
        
           |  |  | 432 | require_once($CFG->dirroot . "/question/engine/renderer.php");
 | 
        
           | 710 | ariadna | 433 | class theme_universe_core_question_renderer extends core_question_renderer
 | 
        
           |  |  | 434 | {
 | 
        
           | 1 | efrain | 435 |     /**
 | 
        
           |  |  | 436 |      * Generate the information bit of the question display that contains the
 | 
        
           |  |  | 437 |      * metadata like the question number, current state, and mark.
 | 
        
           |  |  | 438 |      * @param question_attempt $qa the question attempt to display.
 | 
        
           |  |  | 439 |      * @param qbehaviour_renderer $behaviouroutput the renderer to output the behaviour
 | 
        
           |  |  | 440 |      *      specific parts.
 | 
        
           |  |  | 441 |      * @param qtype_renderer $qtoutput the renderer to output the question type
 | 
        
           |  |  | 442 |      *      specific parts.
 | 
        
           |  |  | 443 |      * @param question_display_options $options controls what should and should not be displayed.
 | 
        
           |  |  | 444 |      * @param string|null $number The question number to display. 'i' is a special
 | 
        
           |  |  | 445 |      *      value that gets displayed as Information. Null means no number is displayed.
 | 
        
           |  |  | 446 |      * @return HTML fragment.
 | 
        
           |  |  | 447 |      */
 | 
        
           |  |  | 448 |     protected function info(
 | 
        
           |  |  | 449 |         question_attempt $qa,
 | 
        
           |  |  | 450 |         qbehaviour_renderer $behaviouroutput,
 | 
        
           |  |  | 451 |         qtype_renderer $qtoutput,
 | 
        
           |  |  | 452 |         question_display_options $options,
 | 
        
           |  |  | 453 |         $number
 | 
        
           |  |  | 454 |     ) {
 | 
        
           |  |  | 455 |         $output = '';
 | 
        
           |  |  | 456 |         $output .= '<div class="d-flex align-items-center flex-wrap mb-sm-2 mb-md-0">' .
 | 
        
           |  |  | 457 |             $this->number($number) .
 | 
        
           |  |  | 458 |             '<div class="d-inline-flex align-items-center flex-wrap">' .
 | 
        
           |  |  | 459 |             $this->status($qa, $behaviouroutput, $options) .
 | 
        
           |  |  | 460 |             $this->mark_summary($qa, $behaviouroutput, $options) .
 | 
        
           |  |  | 461 |             '</div></div>';
 | 
        
           |  |  | 462 |         $output .= '<div>' .
 | 
        
           |  |  | 463 |             $this->question_flag($qa, $options->flags) .
 | 
        
           |  |  | 464 |             $this->edit_question_link($qa, $options) .
 | 
        
           |  |  | 465 |             '</div>';
 | 
        
           |  |  | 466 |         return $output;
 | 
        
           |  |  | 467 |     }
 | 
        
           |  |  | 468 |   | 
        
           |  |  | 469 |     /**
 | 
        
           |  |  | 470 |      * Generate the display of the question number.
 | 
        
           |  |  | 471 |      * @param string|null $number The question number to display. 'i' is a special
 | 
        
           |  |  | 472 |      *      value that gets displayed as Information. Null means no number is displayed.
 | 
        
           |  |  | 473 |      * @return HTML fragment.
 | 
        
           |  |  | 474 |      */
 | 
        
           | 710 | ariadna | 475 |     protected function number($number)
 | 
        
           |  |  | 476 |     {
 | 
        
           | 1 | efrain | 477 |         if (trim($number) === '') {
 | 
        
           |  |  | 478 |             return '';
 | 
        
           |  |  | 479 |         }
 | 
        
           |  |  | 480 |         $numbertext = '';
 | 
        
           |  |  | 481 |         if (trim($number) === 'i') {
 | 
        
           |  |  | 482 |             $numbertext = get_string('information', 'question');
 | 
        
           |  |  | 483 |         } else {
 | 
        
           |  |  | 484 |             $numbertext = get_string(
 | 
        
           |  |  | 485 |                 'questionx',
 | 
        
           |  |  | 486 |                 'question',
 | 
        
           |  |  | 487 |                 html_writer::tag('span', $number, array('class' => 'rui-qno'))
 | 
        
           |  |  | 488 |             );
 | 
        
           |  |  | 489 |         }
 | 
        
           |  |  | 490 |         return html_writer::tag('h4', $numbertext, array('class' => 'h3 w-100 mb-2'));
 | 
        
           |  |  | 491 |     }
 | 
        
           |  |  | 492 |   | 
        
           |  |  | 493 |   | 
        
           |  |  | 494 |     /**
 | 
        
           |  |  | 495 |      * Generate the display of the status line that gives the current state of
 | 
        
           |  |  | 496 |      * the question.
 | 
        
           |  |  | 497 |      * @param question_attempt $qa the question attempt to display.
 | 
        
           |  |  | 498 |      * @param qbehaviour_renderer $behaviouroutput the renderer to output the behaviour
 | 
        
           |  |  | 499 |      *      specific parts.
 | 
        
           |  |  | 500 |      * @param question_display_options $options controls what should and should not be displayed.
 | 
        
           |  |  | 501 |      * @return HTML fragment.
 | 
        
           |  |  | 502 |      */
 | 
        
           |  |  | 503 |     protected function status(
 | 
        
           |  |  | 504 |         question_attempt $qa,
 | 
        
           |  |  | 505 |         qbehaviour_renderer $behaviouroutput,
 | 
        
           |  |  | 506 |         question_display_options $options
 | 
        
           |  |  | 507 |     ) {
 | 
        
           |  |  | 508 |         return html_writer::tag(
 | 
        
           |  |  | 509 |             'div',
 | 
        
           |  |  | 510 |             $qa->get_state_string($options->correctness),
 | 
        
           | 715 | ariadna | 511 |             array('class' => 'state mr-2')
 | 
        
           | 1 | efrain | 512 |         );
 | 
        
           |  |  | 513 |     }
 | 
        
           |  |  | 514 |   | 
        
           |  |  | 515 |     /**
 | 
        
           |  |  | 516 |      * Render the question flag, assuming $flagsoption allows it.
 | 
        
           |  |  | 517 |      *
 | 
        
           |  |  | 518 |      * @param question_attempt $qa the question attempt to display.
 | 
        
           |  |  | 519 |      * @param int $flagsoption the option that says whether flags should be displayed.
 | 
        
           |  |  | 520 |      */
 | 
        
           | 710 | ariadna | 521 |     protected function question_flag(question_attempt $qa, $flagsoption)
 | 
        
           |  |  | 522 |     {
 | 
        
           | 1 | efrain | 523 |         global $CFG;
 | 
        
           |  |  | 524 |   | 
        
           |  |  | 525 |         $divattributes = array('class' => 'questionflag mx-1 d-none');
 | 
        
           |  |  | 526 |   | 
        
           |  |  | 527 |         switch ($flagsoption) {
 | 
        
           |  |  | 528 |             case question_display_options::VISIBLE:
 | 
        
           |  |  | 529 |                 $flagcontent = $this->get_flag_html($qa->is_flagged());
 | 
        
           |  |  | 530 |                 break;
 | 
        
           |  |  | 531 |   | 
        
           |  |  | 532 |             case question_display_options::EDITABLE:
 | 
        
           |  |  | 533 |                 $id = $qa->get_flag_field_name();
 | 
        
           |  |  | 534 |                 $checkboxattributes = array(
 | 
        
           |  |  | 535 |                     'type' => 'checkbox',
 | 
        
           |  |  | 536 |                     'id' => $id . 'checkbox',
 | 
        
           |  |  | 537 |                     'name' => $id,
 | 
        
           |  |  | 538 |                     'value' => 1,
 | 
        
           |  |  | 539 |                 );
 | 
        
           |  |  | 540 |                 if ($qa->is_flagged()) {
 | 
        
           |  |  | 541 |                     $checkboxattributes['checked'] = 'checked';
 | 
        
           |  |  | 542 |                 }
 | 
        
           |  |  | 543 |                 $postdata = question_flags::get_postdata($qa);
 | 
        
           |  |  | 544 |   | 
        
           |  |  | 545 |                 $flagcontent = html_writer::empty_tag(
 | 
        
           |  |  | 546 |                     'input',
 | 
        
           |  |  | 547 |                     array('type' => 'hidden', 'name' => $id, 'value' => 0)
 | 
        
           |  |  | 548 |                 ) .
 | 
        
           |  |  | 549 |                     html_writer::empty_tag('input', $checkboxattributes) .
 | 
        
           |  |  | 550 |                     html_writer::empty_tag(
 | 
        
           |  |  | 551 |                         'input',
 | 
        
           |  |  | 552 |                         array('type' => 'hidden', 'value' => $postdata, 'class' => 'questionflagpostdata')
 | 
        
           |  |  | 553 |                     ) .
 | 
        
           |  |  | 554 |                     html_writer::tag(
 | 
        
           |  |  | 555 |                         'label',
 | 
        
           |  |  | 556 |                         $this->get_flag_html($qa->is_flagged(), $id . 'img'),
 | 
        
           |  |  | 557 |                         array('id' => $id . 'label', 'for' => $id . 'checkbox')
 | 
        
           |  |  | 558 |                     ) . "\n";
 | 
        
           |  |  | 559 |   | 
        
           |  |  | 560 |                 $divattributes = array(
 | 
        
           |  |  | 561 |                     'class' => 'questionflag mb-sm-2 mb-md-0 mx-md-2 editable d-inline-flex',
 | 
        
           |  |  | 562 |                     'aria-atomic' => 'true',
 | 
        
           |  |  | 563 |                     'aria-relevant' => 'text',
 | 
        
           |  |  | 564 |                     'aria-live' => 'assertive',
 | 
        
           |  |  | 565 |                 );
 | 
        
           |  |  | 566 |   | 
        
           |  |  | 567 |                 break;
 | 
        
           |  |  | 568 |   | 
        
           |  |  | 569 |             default:
 | 
        
           |  |  | 570 |                 $flagcontent = '';
 | 
        
           |  |  | 571 |         }
 | 
        
           |  |  | 572 |   | 
        
           |  |  | 573 |         return html_writer::nonempty_tag('div', $flagcontent, $divattributes);
 | 
        
           |  |  | 574 |     }
 | 
        
           |  |  | 575 |   | 
        
           |  |  | 576 |   | 
        
           | 710 | ariadna | 577 |     protected function edit_question_link(question_attempt $qa, question_display_options $options)
 | 
        
           |  |  | 578 |     {
 | 
        
           | 1 | efrain | 579 |         global $CFG;
 | 
        
           |  |  | 580 |   | 
        
           |  |  | 581 |         if (empty($options->editquestionparams)) {
 | 
        
           |  |  | 582 |             return '';
 | 
        
           |  |  | 583 |         }
 | 
        
           |  |  | 584 |   | 
        
           |  |  | 585 |         $params = $options->editquestionparams;
 | 
        
           |  |  | 586 |         if ($params['returnurl'] instanceof moodle_url) {
 | 
        
           |  |  | 587 |             $params['returnurl'] = $params['returnurl']->out_as_local_url(false);
 | 
        
           |  |  | 588 |         }
 | 
        
           |  |  | 589 |         $params['id'] = $qa->get_question_id();
 | 
        
           |  |  | 590 |         $editurl = new moodle_url('/question/bank/editquestion/question.php', $params);
 | 
        
           |  |  | 591 |   | 
        
           |  |  | 592 |         $icon = '<svg width="19" height="19" fill="none" viewBox="0 0 24 24">
 | 
        
           |  |  | 593 |         <path stroke="currentColor"
 | 
        
           |  |  | 594 |             stroke-linecap="round"
 | 
        
           |  |  | 595 |             stroke-linejoin="round"
 | 
        
           |  |  | 596 |             stroke-width="2"
 | 
        
           |  |  | 597 |             d="M4.75 19.25L9 18.25L18.2929 8.95711C18.6834 8.56658 18.6834
 | 
        
           |  |  | 598 |             7.93342 18.2929 7.54289L16.4571 5.70711C16.0666 5.31658 15.4334
 | 
        
           |  |  | 599 |             5.31658 15.0429 5.70711L5.75 15L4.75 19.25Z">
 | 
        
           |  |  | 600 |         </path>
 | 
        
           |  |  | 601 |         <path stroke="currentColor"
 | 
        
           |  |  | 602 |             stroke-linecap="round"
 | 
        
           |  |  | 603 |             stroke-linejoin="round"
 | 
        
           |  |  | 604 |             stroke-width="2"
 | 
        
           |  |  | 605 |             d="M19.25 19.25H13.75">
 | 
        
           |  |  | 606 |         </path>
 | 
        
           |  |  | 607 |         </svg>';
 | 
        
           |  |  | 608 |   | 
        
           | 710 | ariadna | 609 |         return html_writer::link(
 | 
        
           |  |  | 610 |             $editurl,
 | 
        
           |  |  | 611 |             $icon,
 | 
        
           |  |  | 612 |             array('class' => 'btn btn-icon btn-secondary editquestion line-height-1 ml-sm-2')
 | 
        
           |  |  | 613 |         );
 | 
        
           | 1 | efrain | 614 |     }
 | 
        
           |  |  | 615 | }
 |