[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 /** 18 * Defines the renderer for the quiz module. 19 * 20 * @package mod_quiz 21 * @copyright 2011 The Open University 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 29 /** 30 * The renderer for the quiz module. 31 * 32 * @copyright 2011 The Open University 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class mod_quiz_renderer extends plugin_renderer_base { 36 /** 37 * Builds the review page 38 * 39 * @param quiz_attempt $attemptobj an instance of quiz_attempt. 40 * @param array $slots an array of intgers relating to questions. 41 * @param int $page the current page number 42 * @param bool $showall whether to show entire attempt on one page. 43 * @param bool $lastpage if true the current page is the last page. 44 * @param mod_quiz_display_options $displayoptions instance of mod_quiz_display_options. 45 * @param array $summarydata contains all table data 46 * @return $output containing html data. 47 */ 48 public function review_page(quiz_attempt $attemptobj, $slots, $page, $showall, 49 $lastpage, mod_quiz_display_options $displayoptions, 50 $summarydata) { 51 52 $output = ''; 53 $output .= $this->header(); 54 $output .= $this->review_summary_table($summarydata, $page); 55 $output .= $this->review_form($page, $showall, $displayoptions, 56 $this->questions($attemptobj, true, $slots, $page, $showall, $displayoptions), 57 $attemptobj); 58 59 $output .= $this->review_next_navigation($attemptobj, $page, $lastpage, $showall); 60 $output .= $this->footer(); 61 return $output; 62 } 63 64 /** 65 * Renders the review question pop-up. 66 * 67 * @param quiz_attempt $attemptobj an instance of quiz_attempt. 68 * @param int $slot which question to display. 69 * @param int $seq which step of the question attempt to show. null = latest. 70 * @param mod_quiz_display_options $displayoptions instance of mod_quiz_display_options. 71 * @param array $summarydata contains all table data 72 * @return $output containing html data. 73 */ 74 public function review_question_page(quiz_attempt $attemptobj, $slot, $seq, 75 mod_quiz_display_options $displayoptions, $summarydata) { 76 77 $output = ''; 78 $output .= $this->header(); 79 $output .= $this->review_summary_table($summarydata, 0); 80 81 if (!is_null($seq)) { 82 $output .= $attemptobj->render_question_at_step($slot, $seq, true, $this); 83 } else { 84 $output .= $attemptobj->render_question($slot, true, $this); 85 } 86 87 $output .= $this->close_window_button(); 88 $output .= $this->footer(); 89 return $output; 90 } 91 92 /** 93 * Renders the review question pop-up. 94 * 95 * @param quiz_attempt $attemptobj an instance of quiz_attempt. 96 * @param string $message Why the review is not allowed. 97 * @return string html to output. 98 */ 99 public function review_question_not_allowed(quiz_attempt $attemptobj, $message) { 100 $output = ''; 101 $output .= $this->header(); 102 $output .= $this->heading(format_string($attemptobj->get_quiz_name(), true, 103 array("context" => $attemptobj->get_quizobj()->get_context()))); 104 $output .= $this->notification($message); 105 $output .= $this->close_window_button(); 106 $output .= $this->footer(); 107 return $output; 108 } 109 110 /** 111 * Filters the summarydata array. 112 * 113 * @param array $summarydata contains row data for table 114 * @param int $page the current page number 115 * @return $summarydata containing filtered row data 116 */ 117 protected function filter_review_summary_table($summarydata, $page) { 118 if ($page == 0) { 119 return $summarydata; 120 } 121 122 // Only show some of summary table on subsequent pages. 123 foreach ($summarydata as $key => $rowdata) { 124 if (!in_array($key, array('user', 'attemptlist'))) { 125 unset($summarydata[$key]); 126 } 127 } 128 129 return $summarydata; 130 } 131 132 /** 133 * Outputs the table containing data from summary data array 134 * 135 * @param array $summarydata contains row data for table 136 * @param int $page contains the current page number 137 */ 138 public function review_summary_table($summarydata, $page) { 139 $summarydata = $this->filter_review_summary_table($summarydata, $page); 140 if (empty($summarydata)) { 141 return ''; 142 } 143 144 $output = ''; 145 $output .= html_writer::start_tag('table', array( 146 'class' => 'generaltable generalbox quizreviewsummary')); 147 $output .= html_writer::start_tag('tbody'); 148 foreach ($summarydata as $rowdata) { 149 if ($rowdata['title'] instanceof renderable) { 150 $title = $this->render($rowdata['title']); 151 } else { 152 $title = $rowdata['title']; 153 } 154 155 if ($rowdata['content'] instanceof renderable) { 156 $content = $this->render($rowdata['content']); 157 } else { 158 $content = $rowdata['content']; 159 } 160 161 $output .= html_writer::tag('tr', 162 html_writer::tag('th', $title, array('class' => 'cell', 'scope' => 'row')) . 163 html_writer::tag('td', $content, array('class' => 'cell')) 164 ); 165 } 166 167 $output .= html_writer::end_tag('tbody'); 168 $output .= html_writer::end_tag('table'); 169 return $output; 170 } 171 172 /** 173 * Renders each question 174 * 175 * @param quiz_attempt $attemptobj instance of quiz_attempt 176 * @param bool $reviewing 177 * @param array $slots array of intgers relating to questions 178 * @param int $page current page number 179 * @param bool $showall if true shows attempt on single page 180 * @param mod_quiz_display_options $displayoptions instance of mod_quiz_display_options 181 */ 182 public function questions(quiz_attempt $attemptobj, $reviewing, $slots, $page, $showall, 183 mod_quiz_display_options $displayoptions) { 184 $output = ''; 185 foreach ($slots as $slot) { 186 $output .= $attemptobj->render_question($slot, $reviewing, $this, 187 $attemptobj->review_url($slot, $page, $showall)); 188 } 189 return $output; 190 } 191 192 /** 193 * Renders the main bit of the review page. 194 * 195 * @param array $summarydata contain row data for table 196 * @param int $page current page number 197 * @param mod_quiz_display_options $displayoptions instance of mod_quiz_display_options 198 * @param $content contains each question 199 * @param quiz_attempt $attemptobj instance of quiz_attempt 200 * @param bool $showall if true display attempt on one page 201 */ 202 public function review_form($page, $showall, $displayoptions, $content, $attemptobj) { 203 if ($displayoptions->flags != question_display_options::EDITABLE) { 204 return $content; 205 } 206 207 $this->page->requires->js_init_call('M.mod_quiz.init_review_form', null, false, 208 quiz_get_js_module()); 209 210 $output = ''; 211 $output .= html_writer::start_tag('form', array('action' => $attemptobj->review_url(null, 212 $page, $showall), 'method' => 'post', 'class' => 'questionflagsaveform')); 213 $output .= html_writer::start_tag('div'); 214 $output .= $content; 215 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 216 'value' => sesskey())); 217 $output .= html_writer::start_tag('div', array('class' => 'submitbtns')); 218 $output .= html_writer::empty_tag('input', array('type' => 'submit', 219 'class' => 'questionflagsavebutton', 'name' => 'savingflags', 220 'value' => get_string('saveflags', 'question'))); 221 $output .= html_writer::end_tag('div'); 222 $output .= html_writer::end_tag('div'); 223 $output .= html_writer::end_tag('form'); 224 225 return $output; 226 } 227 228 /** 229 * Returns either a liink or button 230 * 231 * @param quiz_attempt $attemptobj instance of quiz_attempt 232 */ 233 public function finish_review_link(quiz_attempt $attemptobj) { 234 $url = $attemptobj->view_url(); 235 236 if ($attemptobj->get_access_manager(time())->attempt_must_be_in_popup()) { 237 $this->page->requires->js_init_call('M.mod_quiz.secure_window.init_close_button', 238 array($url), false, quiz_get_js_module()); 239 return html_writer::empty_tag('input', array('type' => 'button', 240 'value' => get_string('finishreview', 'quiz'), 241 'id' => 'secureclosebutton', 242 'class' => 'mod_quiz-next-nav')); 243 244 } else { 245 return html_writer::link($url, get_string('finishreview', 'quiz'), 246 array('class' => 'mod_quiz-next-nav')); 247 } 248 } 249 250 /** 251 * Creates the navigation links/buttons at the bottom of the reivew attempt page. 252 * 253 * Note, the name of this function is no longer accurate, but when the design 254 * changed, it was decided to keep the old name for backwards compatibility. 255 * 256 * @param quiz_attempt $attemptobj instance of quiz_attempt 257 * @param int $page the current page 258 * @param bool $lastpage if true current page is the last page 259 * @param bool|null $showall if true, the URL will be to review the entire attempt on one page, 260 * and $page will be ignored. If null, a sensible default will be chosen. 261 * 262 * @return string HTML fragment. 263 */ 264 public function review_next_navigation(quiz_attempt $attemptobj, $page, $lastpage, $showall = null) { 265 $nav = ''; 266 if ($page > 0) { 267 $nav .= link_arrow_left(get_string('navigateprevious', 'quiz'), 268 $attemptobj->review_url(null, $page - 1, $showall), false, 'mod_quiz-prev-nav'); 269 } 270 if ($lastpage) { 271 $nav .= $this->finish_review_link($attemptobj); 272 } else { 273 $nav .= link_arrow_right(get_string('navigatenext', 'quiz'), 274 $attemptobj->review_url(null, $page + 1, $showall), false, 'mod_quiz-next-nav'); 275 } 276 return html_writer::tag('div', $nav, array('class' => 'submitbtns')); 277 } 278 279 /** 280 * Return the HTML of the quiz timer. 281 * @return string HTML content. 282 */ 283 public function countdown_timer(quiz_attempt $attemptobj, $timenow) { 284 285 $timeleft = $attemptobj->get_time_left_display($timenow); 286 if ($timeleft !== false) { 287 $ispreview = $attemptobj->is_preview(); 288 $timerstartvalue = $timeleft; 289 if (!$ispreview) { 290 // Make sure the timer starts just above zero. If $timeleft was <= 0, then 291 // this will just have the effect of causing the quiz to be submitted immediately. 292 $timerstartvalue = max($timerstartvalue, 1); 293 } 294 $this->initialise_timer($timerstartvalue, $ispreview); 295 } 296 297 return html_writer::tag('div', get_string('timeleft', 'quiz') . ' ' . 298 html_writer::tag('span', '', array('id' => 'quiz-time-left')), 299 array('id' => 'quiz-timer', 'role' => 'timer', 300 'aria-atomic' => 'true', 'aria-relevant' => 'text')); 301 } 302 303 /** 304 * Create a preview link 305 * 306 * @param $url contains a url to the given page 307 */ 308 public function restart_preview_button($url) { 309 return $this->single_button($url, get_string('startnewpreview', 'quiz')); 310 } 311 312 /** 313 * Outputs the navigation block panel 314 * 315 * @param quiz_nav_panel_base $panel instance of quiz_nav_panel_base 316 */ 317 public function navigation_panel(quiz_nav_panel_base $panel) { 318 319 $output = ''; 320 $userpicture = $panel->user_picture(); 321 if ($userpicture) { 322 $fullname = fullname($userpicture->user); 323 if ($userpicture->size === true) { 324 $fullname = html_writer::div($fullname); 325 } 326 $output .= html_writer::tag('div', $this->render($userpicture) . $fullname, 327 array('id' => 'user-picture', 'class' => 'clearfix')); 328 } 329 $output .= $panel->render_before_button_bits($this); 330 331 $bcc = $panel->get_button_container_class(); 332 $output .= html_writer::start_tag('div', array('class' => "qn_buttons clearfix $bcc")); 333 foreach ($panel->get_question_buttons() as $button) { 334 $output .= $this->render($button); 335 } 336 $output .= html_writer::end_tag('div'); 337 338 $output .= html_writer::tag('div', $panel->render_end_bits($this), 339 array('class' => 'othernav')); 340 341 $this->page->requires->js_init_call('M.mod_quiz.nav.init', null, false, 342 quiz_get_js_module()); 343 344 return $output; 345 } 346 347 /** 348 * Display a quiz navigation button. 349 * 350 * @param quiz_nav_question_button $button 351 * @return string HTML fragment. 352 */ 353 protected function render_quiz_nav_question_button(quiz_nav_question_button $button) { 354 $classes = array('qnbutton', $button->stateclass, $button->navmethod); 355 $extrainfo = array(); 356 357 if ($button->currentpage) { 358 $classes[] = 'thispage'; 359 $extrainfo[] = get_string('onthispage', 'quiz'); 360 } 361 362 // Flagged? 363 if ($button->flagged) { 364 $classes[] = 'flagged'; 365 $flaglabel = get_string('flagged', 'question'); 366 } else { 367 $flaglabel = ''; 368 } 369 $extrainfo[] = html_writer::tag('span', $flaglabel, array('class' => 'flagstate')); 370 371 if (is_numeric($button->number)) { 372 $qnostring = 'questionnonav'; 373 } else { 374 $qnostring = 'questionnonavinfo'; 375 } 376 377 $a = new stdClass(); 378 $a->number = $button->number; 379 $a->attributes = implode(' ', $extrainfo); 380 $tagcontents = html_writer::tag('span', '', array('class' => 'thispageholder')) . 381 html_writer::tag('span', '', array('class' => 'trafficlight')) . 382 get_string($qnostring, 'quiz', $a); 383 $tagattributes = array('class' => implode(' ', $classes), 'id' => $button->id, 384 'title' => $button->statestring, 'data-quiz-page' => $button->page); 385 386 if ($button->url) { 387 return html_writer::link($button->url, $tagcontents, $tagattributes); 388 } else { 389 return html_writer::tag('span', $tagcontents, $tagattributes); 390 } 391 } 392 393 /** 394 * Display a quiz navigation heading. 395 * 396 * @param quiz_nav_section_heading $heading the heading. 397 * @return string HTML fragment. 398 */ 399 protected function render_quiz_nav_section_heading(quiz_nav_section_heading $heading) { 400 return $this->heading($heading->heading, 3, 'mod_quiz-section-heading'); 401 } 402 403 /** 404 * outputs the link the other attempts. 405 * 406 * @param mod_quiz_links_to_other_attempts $links 407 */ 408 protected function render_mod_quiz_links_to_other_attempts( 409 mod_quiz_links_to_other_attempts $links) { 410 $attemptlinks = array(); 411 foreach ($links->links as $attempt => $url) { 412 if (!$url) { 413 $attemptlinks[] = html_writer::tag('strong', $attempt); 414 } else if ($url instanceof renderable) { 415 $attemptlinks[] = $this->render($url); 416 } else { 417 $attemptlinks[] = html_writer::link($url, $attempt); 418 } 419 } 420 return implode(', ', $attemptlinks); 421 } 422 423 public function start_attempt_page(quiz $quizobj, mod_quiz_preflight_check_form $mform) { 424 $output = ''; 425 $output .= $this->header(); 426 $output .= $this->heading(format_string($quizobj->get_quiz_name(), true, 427 array("context" => $quizobj->get_context()))); 428 $output .= $this->quiz_intro($quizobj->get_quiz(), $quizobj->get_cm()); 429 $output .= $mform->render(); 430 $output .= $this->footer(); 431 return $output; 432 } 433 434 /** 435 * Attempt Page 436 * 437 * @param quiz_attempt $attemptobj Instance of quiz_attempt 438 * @param int $page Current page number 439 * @param quiz_access_manager $accessmanager Instance of quiz_access_manager 440 * @param array $messages An array of messages 441 * @param array $slots Contains an array of integers that relate to questions 442 * @param int $id The ID of an attempt 443 * @param int $nextpage The number of the next page 444 */ 445 public function attempt_page($attemptobj, $page, $accessmanager, $messages, $slots, $id, 446 $nextpage) { 447 $output = ''; 448 $output .= $this->header(); 449 $output .= $this->quiz_notices($messages); 450 $output .= $this->attempt_form($attemptobj, $page, $slots, $id, $nextpage); 451 $output .= $this->footer(); 452 return $output; 453 } 454 455 /** 456 * Returns any notices. 457 * 458 * @param array $messages 459 */ 460 public function quiz_notices($messages) { 461 if (!$messages) { 462 return ''; 463 } 464 return $this->box($this->heading(get_string('accessnoticesheader', 'quiz'), 3) . 465 $this->access_messages($messages), 'quizaccessnotices'); 466 } 467 468 /** 469 * Ouputs the form for making an attempt 470 * 471 * @param quiz_attempt $attemptobj 472 * @param int $page Current page number 473 * @param array $slots Array of integers relating to questions 474 * @param int $id ID of the attempt 475 * @param int $nextpage Next page number 476 */ 477 public function attempt_form($attemptobj, $page, $slots, $id, $nextpage) { 478 $output = ''; 479 480 // Start the form. 481 $output .= html_writer::start_tag('form', 482 array('action' => $attemptobj->processattempt_url(), 'method' => 'post', 483 'enctype' => 'multipart/form-data', 'accept-charset' => 'utf-8', 484 'id' => 'responseform')); 485 $output .= html_writer::start_tag('div'); 486 487 // Print all the questions. 488 foreach ($slots as $slot) { 489 $output .= $attemptobj->render_question($slot, false, $this, 490 $attemptobj->attempt_url($slot, $page), $this); 491 } 492 493 $output .= $this->attempt_navigation_buttons($page, $attemptobj->is_last_page($page)); 494 495 // Some hidden fields to trach what is going on. 496 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'attempt', 497 'value' => $attemptobj->get_attemptid())); 498 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'thispage', 499 'value' => $page, 'id' => 'followingpage')); 500 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'nextpage', 501 'value' => $nextpage)); 502 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'timeup', 503 'value' => '0', 'id' => 'timeup')); 504 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 505 'value' => sesskey())); 506 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'scrollpos', 507 'value' => '', 'id' => 'scrollpos')); 508 509 // Add a hidden field with questionids. Do this at the end of the form, so 510 // if you navigate before the form has finished loading, it does not wipe all 511 // the student's answers. 512 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'slots', 513 'value' => implode(',', $attemptobj->get_active_slots($page)))); 514 515 // Finish the form. 516 $output .= html_writer::end_tag('div'); 517 $output .= html_writer::end_tag('form'); 518 519 $output .= $this->connection_warning(); 520 521 return $output; 522 } 523 524 /** 525 * Display the prev/next buttons that go at the bottom of each page of the attempt. 526 * 527 * @param int $page the page number. Starts at 0 for the first page. 528 * @param bool $lastpage is this the last page in the quiz? 529 * @return string HTML fragment. 530 */ 531 protected function attempt_navigation_buttons($page, $lastpage) { 532 $output = ''; 533 534 $output .= html_writer::start_tag('div', array('class' => 'submitbtns')); 535 if ($page > 0) { 536 $output .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'previous', 537 'value' => get_string('navigateprevious', 'quiz'), 'class' => 'mod_quiz-prev-nav')); 538 } 539 if ($lastpage) { 540 $nextlabel = get_string('endtest', 'quiz'); 541 } else { 542 $nextlabel = get_string('navigatenext', 'quiz'); 543 } 544 $output .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'next', 545 'value' => $nextlabel, 'class' => 'mod_quiz-next-nav')); 546 $output .= html_writer::end_tag('div'); 547 548 return $output; 549 } 550 551 /** 552 * Render a button which allows students to redo a question in the attempt. 553 * 554 * @param int $slot the number of the slot to generate the button for. 555 * @param bool $disabled if true, output the button disabled. 556 * @return string HTML fragment. 557 */ 558 public function redo_question_button($slot, $disabled) { 559 $attributes = array('type' => 'submit', 'name' => 'redoslot' . $slot, 560 'value' => get_string('redoquestion', 'quiz'), 'class' => 'mod_quiz-redo_question_button'); 561 if ($disabled) { 562 $attributes['disabled'] = 'disabled'; 563 } 564 return html_writer::div(html_writer::empty_tag('input', $attributes)); 565 } 566 567 /** 568 * Output the JavaScript required to initialise the countdown timer. 569 * @param int $timerstartvalue time remaining, in seconds. 570 */ 571 public function initialise_timer($timerstartvalue, $ispreview) { 572 $options = array($timerstartvalue, (bool)$ispreview); 573 $this->page->requires->js_init_call('M.mod_quiz.timer.init', $options, false, quiz_get_js_module()); 574 } 575 576 /** 577 * Output a page with an optional message, and JavaScript code to close the 578 * current window and redirect the parent window to a new URL. 579 * @param moodle_url $url the URL to redirect the parent window to. 580 * @param string $message message to display before closing the window. (optional) 581 * @return string HTML to output. 582 */ 583 public function close_attempt_popup($url, $message = '') { 584 $output = ''; 585 $output .= $this->header(); 586 $output .= $this->box_start(); 587 588 if ($message) { 589 $output .= html_writer::tag('p', $message); 590 $output .= html_writer::tag('p', get_string('windowclosing', 'quiz')); 591 $delay = 5; 592 } else { 593 $output .= html_writer::tag('p', get_string('pleaseclose', 'quiz')); 594 $delay = 0; 595 } 596 $this->page->requires->js_init_call('M.mod_quiz.secure_window.close', 597 array($url, $delay), false, quiz_get_js_module()); 598 599 $output .= $this->box_end(); 600 $output .= $this->footer(); 601 return $output; 602 } 603 604 /** 605 * Print each message in an array, surrounded by <p>, </p> tags. 606 * 607 * @param array $messages the array of message strings. 608 * @param bool $return if true, return a string, instead of outputting. 609 * 610 * @return string HTML to output. 611 */ 612 public function access_messages($messages) { 613 $output = ''; 614 foreach ($messages as $message) { 615 $output .= html_writer::tag('p', $message) . "\n"; 616 } 617 return $output; 618 } 619 620 /* 621 * Summary Page 622 */ 623 /** 624 * Create the summary page 625 * 626 * @param quiz_attempt $attemptobj 627 * @param mod_quiz_display_options $displayoptions 628 */ 629 public function summary_page($attemptobj, $displayoptions) { 630 $output = ''; 631 $output .= $this->header(); 632 $output .= $this->heading(format_string($attemptobj->get_quiz_name())); 633 $output .= $this->heading(get_string('summaryofattempt', 'quiz'), 3); 634 $output .= $this->summary_table($attemptobj, $displayoptions); 635 $output .= $this->summary_page_controls($attemptobj); 636 $output .= $this->footer(); 637 return $output; 638 } 639 640 /** 641 * Generates the table of summarydata 642 * 643 * @param quiz_attempt $attemptobj 644 * @param mod_quiz_display_options $displayoptions 645 */ 646 public function summary_table($attemptobj, $displayoptions) { 647 // Prepare the summary table header. 648 $table = new html_table(); 649 $table->attributes['class'] = 'generaltable quizsummaryofattempt boxaligncenter'; 650 $table->head = array(get_string('question', 'quiz'), get_string('status', 'quiz')); 651 $table->align = array('left', 'left'); 652 $table->size = array('', ''); 653 $markscolumn = $displayoptions->marks >= question_display_options::MARK_AND_MAX; 654 if ($markscolumn) { 655 $table->head[] = get_string('marks', 'quiz'); 656 $table->align[] = 'left'; 657 $table->size[] = ''; 658 } 659 $tablewidth = count($table->align); 660 $table->data = array(); 661 662 // Get the summary info for each question. 663 $slots = $attemptobj->get_slots(); 664 foreach ($slots as $slot) { 665 // Add a section headings if we need one here. 666 $heading = $attemptobj->get_heading_before_slot($slot); 667 if ($heading) { 668 $cell = new html_table_cell(format_string($heading)); 669 $cell->header = true; 670 $cell->colspan = $tablewidth; 671 $table->data[] = array($cell); 672 $table->rowclasses[] = 'quizsummaryheading'; 673 } 674 675 // Don't display information items. 676 if (!$attemptobj->is_real_question($slot)) { 677 continue; 678 } 679 680 // Real question, show it. 681 $flag = ''; 682 if ($attemptobj->is_question_flagged($slot)) { 683 $flag = html_writer::empty_tag('img', array('src' => $this->pix_url('i/flagged'), 684 'alt' => get_string('flagged', 'question'), 'class' => 'questionflag icon-post')); 685 } 686 if ($attemptobj->can_navigate_to($slot)) { 687 $row = array(html_writer::link($attemptobj->attempt_url($slot), 688 $attemptobj->get_question_number($slot) . $flag), 689 $attemptobj->get_question_status($slot, $displayoptions->correctness)); 690 } else { 691 $row = array($attemptobj->get_question_number($slot) . $flag, 692 $attemptobj->get_question_status($slot, $displayoptions->correctness)); 693 } 694 if ($markscolumn) { 695 $row[] = $attemptobj->get_question_mark($slot); 696 } 697 $table->data[] = $row; 698 $table->rowclasses[] = 'quizsummary' . $slot . ' ' . $attemptobj->get_question_state_class( 699 $slot, $displayoptions->correctness); 700 } 701 702 // Print the summary table. 703 $output = html_writer::table($table); 704 705 return $output; 706 } 707 708 /** 709 * Creates any controls a the page should have. 710 * 711 * @param quiz_attempt $attemptobj 712 */ 713 public function summary_page_controls($attemptobj) { 714 $output = ''; 715 716 // Return to place button. 717 if ($attemptobj->get_state() == quiz_attempt::IN_PROGRESS) { 718 $button = new single_button( 719 new moodle_url($attemptobj->attempt_url(null, $attemptobj->get_currentpage())), 720 get_string('returnattempt', 'quiz')); 721 $output .= $this->container($this->container($this->render($button), 722 'controls'), 'submitbtns mdl-align'); 723 } 724 725 // Finish attempt button. 726 $options = array( 727 'attempt' => $attemptobj->get_attemptid(), 728 'finishattempt' => 1, 729 'timeup' => 0, 730 'slots' => '', 731 'sesskey' => sesskey(), 732 ); 733 734 $button = new single_button( 735 new moodle_url($attemptobj->processattempt_url(), $options), 736 get_string('submitallandfinish', 'quiz')); 737 $button->id = 'responseform'; 738 if ($attemptobj->get_state() == quiz_attempt::IN_PROGRESS) { 739 $button->add_action(new confirm_action(get_string('confirmclose', 'quiz'), null, 740 get_string('submitallandfinish', 'quiz'))); 741 } 742 743 $duedate = $attemptobj->get_due_date(); 744 $message = ''; 745 if ($attemptobj->get_state() == quiz_attempt::OVERDUE) { 746 $message = get_string('overduemustbesubmittedby', 'quiz', userdate($duedate)); 747 748 } else if ($duedate) { 749 $message = get_string('mustbesubmittedby', 'quiz', userdate($duedate)); 750 } 751 752 $output .= $this->countdown_timer($attemptobj, time()); 753 $output .= $this->container($message . $this->container( 754 $this->render($button), 'controls'), 'submitbtns mdl-align'); 755 756 return $output; 757 } 758 759 /* 760 * View Page 761 */ 762 /** 763 * Generates the view page 764 * 765 * @param int $course The id of the course 766 * @param array $quiz Array conting quiz data 767 * @param int $cm Course Module ID 768 * @param int $context The page context ID 769 * @param array $infomessages information about this quiz 770 * @param mod_quiz_view_object $viewobj 771 * @param string $buttontext text for the start/continue attempt button, if 772 * it should be shown. 773 * @param array $infomessages further information about why the student cannot 774 * attempt this quiz now, if appicable this quiz 775 */ 776 public function view_page($course, $quiz, $cm, $context, $viewobj) { 777 $output = ''; 778 $output .= $this->view_information($quiz, $cm, $context, $viewobj->infomessages); 779 $output .= $this->view_table($quiz, $context, $viewobj); 780 $output .= $this->view_result_info($quiz, $context, $cm, $viewobj); 781 $output .= $this->box($this->view_page_buttons($viewobj), 'quizattempt'); 782 return $output; 783 } 784 785 /** 786 * Work out, and render, whatever buttons, and surrounding info, should appear 787 * at the end of the review page. 788 * @param mod_quiz_view_object $viewobj the information required to display 789 * the view page. 790 * @return string HTML to output. 791 */ 792 public function view_page_buttons(mod_quiz_view_object $viewobj) { 793 global $CFG; 794 $output = ''; 795 796 if (!$viewobj->quizhasquestions) { 797 $output .= $this->no_questions_message($viewobj->canedit, $viewobj->editurl); 798 } 799 800 $output .= $this->access_messages($viewobj->preventmessages); 801 802 if ($viewobj->buttontext) { 803 $output .= $this->start_attempt_button($viewobj->buttontext, 804 $viewobj->startattempturl, $viewobj->preflightcheckform, 805 $viewobj->popuprequired, $viewobj->popupoptions); 806 } 807 808 if ($viewobj->showbacktocourse) { 809 $output .= $this->single_button($viewobj->backtocourseurl, 810 get_string('backtocourse', 'quiz'), 'get', 811 array('class' => 'continuebutton')); 812 } 813 814 return $output; 815 } 816 817 /** 818 * Generates the view attempt button 819 * 820 * @param string $buttontext the label to display on the button. 821 * @param moodle_url $url The URL to POST to in order to start the attempt. 822 * @param mod_quiz_preflight_check_form $preflightcheckform deprecated. 823 * @param bool $popuprequired whether the attempt needs to be opened in a pop-up. 824 * @param array $popupoptions the options to use if we are opening a popup. 825 * @return string HTML fragment. 826 */ 827 public function start_attempt_button($buttontext, moodle_url $url, 828 mod_quiz_preflight_check_form $preflightcheckform = null, 829 $popuprequired = false, $popupoptions = null) { 830 831 if (is_string($preflightcheckform)) { 832 // Calling code was not updated since the API change. 833 debugging('The third argument to start_attempt_button should now be the ' . 834 'mod_quiz_preflight_check_form from ' . 835 'quiz_access_manager::get_preflight_check_form, not a warning message string.'); 836 } 837 838 $button = new single_button($url, $buttontext); 839 $button->class .= ' quizstartbuttondiv'; 840 841 $popupjsoptions = null; 842 if ($popuprequired && $popupoptions) { 843 $action = new popup_action('click', $url, 'popup', $popupoptions); 844 $popupjsoptions = $action->get_js_options(); 845 } 846 847 if ($preflightcheckform) { 848 $checkform = $preflightcheckform->render(); 849 } else { 850 $checkform = null; 851 } 852 853 $this->page->requires->js_call_amd('mod_quiz/preflightcheck', 'init', 854 array('.quizstartbuttondiv input[type=submit]', get_string('startattempt', 'quiz'), 855 '#mod_quiz_preflight_form', $popupjsoptions)); 856 857 return $this->render($button) . $checkform; 858 } 859 860 /** 861 * Generate a message saying that this quiz has no questions, with a button to 862 * go to the edit page, if the user has the right capability. 863 * @param object $quiz the quiz settings. 864 * @param object $cm the course_module object. 865 * @param object $context the quiz context. 866 * @return string HTML to output. 867 */ 868 public function no_questions_message($canedit, $editurl) { 869 $output = ''; 870 $output .= $this->notification(get_string('noquestions', 'quiz')); 871 if ($canedit) { 872 $output .= $this->single_button($editurl, get_string('editquiz', 'quiz'), 'get'); 873 } 874 875 return $output; 876 } 877 878 /** 879 * Outputs an error message for any guests accessing the quiz 880 * 881 * @param int $course The course ID 882 * @param array $quiz Array contingin quiz data 883 * @param int $cm Course Module ID 884 * @param int $context The page contect ID 885 * @param array $messages Array containing any messages 886 */ 887 public function view_page_guest($course, $quiz, $cm, $context, $messages) { 888 $output = ''; 889 $output .= $this->view_information($quiz, $cm, $context, $messages); 890 $guestno = html_writer::tag('p', get_string('guestsno', 'quiz')); 891 $liketologin = html_writer::tag('p', get_string('liketologin')); 892 $referer = get_local_referer(false); 893 $output .= $this->confirm($guestno."\n\n".$liketologin."\n", get_login_url(), $referer); 894 return $output; 895 } 896 897 /** 898 * Outputs and error message for anyone who is not enrolle don the course 899 * 900 * @param int $course The course ID 901 * @param array $quiz Array contingin quiz data 902 * @param int $cm Course Module ID 903 * @param int $context The page contect ID 904 * @param array $messages Array containing any messages 905 */ 906 public function view_page_notenrolled($course, $quiz, $cm, $context, $messages) { 907 global $CFG; 908 $output = ''; 909 $output .= $this->view_information($quiz, $cm, $context, $messages); 910 $youneedtoenrol = html_writer::tag('p', get_string('youneedtoenrol', 'quiz')); 911 $button = html_writer::tag('p', 912 $this->continue_button($CFG->wwwroot . '/course/view.php?id=' . $course->id)); 913 $output .= $this->box($youneedtoenrol."\n\n".$button."\n", 'generalbox', 'notice'); 914 return $output; 915 } 916 917 /** 918 * Output the page information 919 * 920 * @param object $quiz the quiz settings. 921 * @param object $cm the course_module object. 922 * @param object $context the quiz context. 923 * @param array $messages any access messages that should be described. 924 * @return string HTML to output. 925 */ 926 public function view_information($quiz, $cm, $context, $messages) { 927 global $CFG; 928 929 $output = ''; 930 931 // Print quiz name and description. 932 $output .= $this->heading(format_string($quiz->name)); 933 $output .= $this->quiz_intro($quiz, $cm); 934 935 // Output any access messages. 936 if ($messages) { 937 $output .= $this->box($this->access_messages($messages), 'quizinfo'); 938 } 939 940 // Show number of attempts summary to those who can view reports. 941 if (has_capability('mod/quiz:viewreports', $context)) { 942 if ($strattemptnum = $this->quiz_attempt_summary_link_to_reports($quiz, $cm, 943 $context)) { 944 $output .= html_writer::tag('div', $strattemptnum, 945 array('class' => 'quizattemptcounts')); 946 } 947 } 948 return $output; 949 } 950 951 /** 952 * Output the quiz intro. 953 * @param object $quiz the quiz settings. 954 * @param object $cm the course_module object. 955 * @return string HTML to output. 956 */ 957 public function quiz_intro($quiz, $cm) { 958 if (html_is_blank($quiz->intro)) { 959 return ''; 960 } 961 962 return $this->box(format_module_intro('quiz', $quiz, $cm->id), 'generalbox', 'intro'); 963 } 964 965 /** 966 * Generates the table heading. 967 */ 968 public function view_table_heading() { 969 return $this->heading(get_string('summaryofattempts', 'quiz'), 3); 970 } 971 972 /** 973 * Generates the table of data 974 * 975 * @param array $quiz Array contining quiz data 976 * @param int $context The page context ID 977 * @param mod_quiz_view_object $viewobj 978 */ 979 public function view_table($quiz, $context, $viewobj) { 980 if (!$viewobj->attempts) { 981 return ''; 982 } 983 984 // Prepare table header. 985 $table = new html_table(); 986 $table->attributes['class'] = 'generaltable quizattemptsummary'; 987 $table->head = array(); 988 $table->align = array(); 989 $table->size = array(); 990 if ($viewobj->attemptcolumn) { 991 $table->head[] = get_string('attemptnumber', 'quiz'); 992 $table->align[] = 'center'; 993 $table->size[] = ''; 994 } 995 $table->head[] = get_string('attemptstate', 'quiz'); 996 $table->align[] = 'left'; 997 $table->size[] = ''; 998 if ($viewobj->markcolumn) { 999 $table->head[] = get_string('marks', 'quiz') . ' / ' . 1000 quiz_format_grade($quiz, $quiz->sumgrades); 1001 $table->align[] = 'center'; 1002 $table->size[] = ''; 1003 } 1004 if ($viewobj->gradecolumn) { 1005 $table->head[] = get_string('grade') . ' / ' . 1006 quiz_format_grade($quiz, $quiz->grade); 1007 $table->align[] = 'center'; 1008 $table->size[] = ''; 1009 } 1010 if ($viewobj->canreviewmine) { 1011 $table->head[] = get_string('review', 'quiz'); 1012 $table->align[] = 'center'; 1013 $table->size[] = ''; 1014 } 1015 if ($viewobj->feedbackcolumn) { 1016 $table->head[] = get_string('feedback', 'quiz'); 1017 $table->align[] = 'left'; 1018 $table->size[] = ''; 1019 } 1020 1021 // One row for each attempt. 1022 foreach ($viewobj->attemptobjs as $attemptobj) { 1023 $attemptoptions = $attemptobj->get_display_options(true); 1024 $row = array(); 1025 1026 // Add the attempt number. 1027 if ($viewobj->attemptcolumn) { 1028 if ($attemptobj->is_preview()) { 1029 $row[] = get_string('preview', 'quiz'); 1030 } else { 1031 $row[] = $attemptobj->get_attempt_number(); 1032 } 1033 } 1034 1035 $row[] = $this->attempt_state($attemptobj); 1036 1037 if ($viewobj->markcolumn) { 1038 if ($attemptoptions->marks >= question_display_options::MARK_AND_MAX && 1039 $attemptobj->is_finished()) { 1040 $row[] = quiz_format_grade($quiz, $attemptobj->get_sum_marks()); 1041 } else { 1042 $row[] = ''; 1043 } 1044 } 1045 1046 // Ouside the if because we may be showing feedback but not grades. 1047 $attemptgrade = quiz_rescale_grade($attemptobj->get_sum_marks(), $quiz, false); 1048 1049 if ($viewobj->gradecolumn) { 1050 if ($attemptoptions->marks >= question_display_options::MARK_AND_MAX && 1051 $attemptobj->is_finished()) { 1052 1053 // Highlight the highest grade if appropriate. 1054 if ($viewobj->overallstats && !$attemptobj->is_preview() 1055 && $viewobj->numattempts > 1 && !is_null($viewobj->mygrade) 1056 && $attemptobj->get_state() == quiz_attempt::FINISHED 1057 && $attemptgrade == $viewobj->mygrade 1058 && $quiz->grademethod == QUIZ_GRADEHIGHEST) { 1059 $table->rowclasses[$attemptobj->get_attempt_number()] = 'bestrow'; 1060 } 1061 1062 $row[] = quiz_format_grade($quiz, $attemptgrade); 1063 } else { 1064 $row[] = ''; 1065 } 1066 } 1067 1068 if ($viewobj->canreviewmine) { 1069 $row[] = $viewobj->accessmanager->make_review_link($attemptobj->get_attempt(), 1070 $attemptoptions, $this); 1071 } 1072 1073 if ($viewobj->feedbackcolumn && $attemptobj->is_finished()) { 1074 if ($attemptoptions->overallfeedback) { 1075 $row[] = quiz_feedback_for_grade($attemptgrade, $quiz, $context); 1076 } else { 1077 $row[] = ''; 1078 } 1079 } 1080 1081 if ($attemptobj->is_preview()) { 1082 $table->data['preview'] = $row; 1083 } else { 1084 $table->data[$attemptobj->get_attempt_number()] = $row; 1085 } 1086 } // End of loop over attempts. 1087 1088 $output = ''; 1089 $output .= $this->view_table_heading(); 1090 $output .= html_writer::table($table); 1091 return $output; 1092 } 1093 1094 /** 1095 * Generate a brief textual desciption of the current state of an attempt. 1096 * @param quiz_attempt $attemptobj the attempt 1097 * @param int $timenow the time to use as 'now'. 1098 * @return string the appropriate lang string to describe the state. 1099 */ 1100 public function attempt_state($attemptobj) { 1101 switch ($attemptobj->get_state()) { 1102 case quiz_attempt::IN_PROGRESS: 1103 return get_string('stateinprogress', 'quiz'); 1104 1105 case quiz_attempt::OVERDUE: 1106 return get_string('stateoverdue', 'quiz') . html_writer::tag('span', 1107 get_string('stateoverduedetails', 'quiz', 1108 userdate($attemptobj->get_due_date())), 1109 array('class' => 'statedetails')); 1110 1111 case quiz_attempt::FINISHED: 1112 return get_string('statefinished', 'quiz') . html_writer::tag('span', 1113 get_string('statefinisheddetails', 'quiz', 1114 userdate($attemptobj->get_submitted_date())), 1115 array('class' => 'statedetails')); 1116 1117 case quiz_attempt::ABANDONED: 1118 return get_string('stateabandoned', 'quiz'); 1119 } 1120 } 1121 1122 /** 1123 * Generates data pertaining to quiz results 1124 * 1125 * @param array $quiz Array containing quiz data 1126 * @param int $context The page context ID 1127 * @param int $cm The Course Module Id 1128 * @param mod_quiz_view_object $viewobj 1129 */ 1130 public function view_result_info($quiz, $context, $cm, $viewobj) { 1131 $output = ''; 1132 if (!$viewobj->numattempts && !$viewobj->gradecolumn && is_null($viewobj->mygrade)) { 1133 return $output; 1134 } 1135 $resultinfo = ''; 1136 1137 if ($viewobj->overallstats) { 1138 if ($viewobj->moreattempts) { 1139 $a = new stdClass(); 1140 $a->method = quiz_get_grading_option_name($quiz->grademethod); 1141 $a->mygrade = quiz_format_grade($quiz, $viewobj->mygrade); 1142 $a->quizgrade = quiz_format_grade($quiz, $quiz->grade); 1143 $resultinfo .= $this->heading(get_string('gradesofar', 'quiz', $a), 3); 1144 } else { 1145 $a = new stdClass(); 1146 $a->grade = quiz_format_grade($quiz, $viewobj->mygrade); 1147 $a->maxgrade = quiz_format_grade($quiz, $quiz->grade); 1148 $a = get_string('outofshort', 'quiz', $a); 1149 $resultinfo .= $this->heading(get_string('yourfinalgradeis', 'quiz', $a), 3); 1150 } 1151 } 1152 1153 if ($viewobj->mygradeoverridden) { 1154 1155 $resultinfo .= html_writer::tag('p', get_string('overriddennotice', 'grades'), 1156 array('class' => 'overriddennotice'))."\n"; 1157 } 1158 if ($viewobj->gradebookfeedback) { 1159 $resultinfo .= $this->heading(get_string('comment', 'quiz'), 3); 1160 $resultinfo .= html_writer::div($viewobj->gradebookfeedback, 'quizteacherfeedback') . "\n"; 1161 } 1162 if ($viewobj->feedbackcolumn) { 1163 $resultinfo .= $this->heading(get_string('overallfeedback', 'quiz'), 3); 1164 $resultinfo .= html_writer::div( 1165 quiz_feedback_for_grade($viewobj->mygrade, $quiz, $context), 1166 'quizgradefeedback') . "\n"; 1167 } 1168 1169 if ($resultinfo) { 1170 $output .= $this->box($resultinfo, 'generalbox', 'feedback'); 1171 } 1172 return $output; 1173 } 1174 1175 /** 1176 * Output either a link to the review page for an attempt, or a button to 1177 * open the review in a popup window. 1178 * 1179 * @param moodle_url $url of the target page. 1180 * @param bool $reviewinpopup whether a pop-up is required. 1181 * @param array $popupoptions options to pass to the popup_action constructor. 1182 * @return string HTML to output. 1183 */ 1184 public function review_link($url, $reviewinpopup, $popupoptions) { 1185 if ($reviewinpopup) { 1186 $button = new single_button($url, get_string('review', 'quiz')); 1187 $button->add_action(new popup_action('click', $url, 'quizpopup', $popupoptions)); 1188 return $this->render($button); 1189 1190 } else { 1191 return html_writer::link($url, get_string('review', 'quiz'), 1192 array('title' => get_string('reviewthisattempt', 'quiz'))); 1193 } 1194 } 1195 1196 /** 1197 * Displayed where there might normally be a review link, to explain why the 1198 * review is not available at this time. 1199 * @param string $message optional message explaining why the review is not possible. 1200 * @return string HTML to output. 1201 */ 1202 public function no_review_message($message) { 1203 return html_writer::nonempty_tag('span', $message, 1204 array('class' => 'noreviewmessage')); 1205 } 1206 1207 /** 1208 * Returns the same as {@link quiz_num_attempt_summary()} but wrapped in a link 1209 * to the quiz reports. 1210 * 1211 * @param object $quiz the quiz object. Only $quiz->id is used at the moment. 1212 * @param object $cm the cm object. Only $cm->course, $cm->groupmode and $cm->groupingid 1213 * fields are used at the moment. 1214 * @param object $context the quiz context. 1215 * @param bool $returnzero if false (default), when no attempts have been made '' is returned 1216 * instead of 'Attempts: 0'. 1217 * @param int $currentgroup if there is a concept of current group where this method is being 1218 * called 1219 * (e.g. a report) pass it in here. Default 0 which means no current group. 1220 * @return string HTML fragment for the link. 1221 */ 1222 public function quiz_attempt_summary_link_to_reports($quiz, $cm, $context, 1223 $returnzero = false, $currentgroup = 0) { 1224 global $CFG; 1225 $summary = quiz_num_attempt_summary($quiz, $cm, $returnzero, $currentgroup); 1226 if (!$summary) { 1227 return ''; 1228 } 1229 1230 require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php'); 1231 $url = new moodle_url('/mod/quiz/report.php', array( 1232 'id' => $cm->id, 'mode' => quiz_report_default_report($context))); 1233 return html_writer::link($url, $summary); 1234 } 1235 1236 /** 1237 * Outputs a chart. 1238 * 1239 * @param \core\chart_base $chart The chart. 1240 * @param string $title The title to display above the graph. 1241 * @return string HTML fragment for the graph. 1242 */ 1243 public function chart(\core\chart_base $chart, $title) { 1244 return $this->heading($title, 3) . html_writer::tag('div', $this->render($chart), array('class' => 'graph')); 1245 } 1246 1247 /** 1248 * Output a graph, or a message saying that GD is required. 1249 * @param moodle_url $url the URL of the graph. 1250 * @param string $title the title to display above the graph. 1251 * @return string HTML fragment for the graph. 1252 */ 1253 public function graph(moodle_url $url, $title) { 1254 global $CFG; 1255 1256 $graph = html_writer::empty_tag('img', array('src' => $url, 'alt' => $title)); 1257 1258 return $this->heading($title, 3) . html_writer::tag('div', $graph, array('class' => 'graph')); 1259 } 1260 1261 /** 1262 * Output the connection warning messages, which are initially hidden, and 1263 * only revealed by JavaScript if necessary. 1264 */ 1265 public function connection_warning() { 1266 $options = array('filter' => false, 'newlines' => false); 1267 $warning = format_text(get_string('connectionerror', 'quiz'), FORMAT_MARKDOWN, $options); 1268 $ok = format_text(get_string('connectionok', 'quiz'), FORMAT_MARKDOWN, $options); 1269 return html_writer::tag('div', $warning, 1270 array('id' => 'connection-error', 'style' => 'display: none;', 'role' => 'alert')) . 1271 html_writer::tag('div', $ok, array('id' => 'connection-ok', 'style' => 'display: none;', 'role' => 'alert')); 1272 } 1273 } 1274 1275 1276 class mod_quiz_links_to_other_attempts implements renderable { 1277 /** 1278 * @var array string attempt number => url, or null for the current attempt. 1279 * url may be either a moodle_url, or a renderable. 1280 */ 1281 public $links = array(); 1282 } 1283 1284 1285 class mod_quiz_view_object { 1286 /** @var array $infomessages of messages with information to display about the quiz. */ 1287 public $infomessages; 1288 /** @var array $attempts contains all the user's attempts at this quiz. */ 1289 public $attempts; 1290 /** @var array $attemptobjs quiz_attempt objects corresponding to $attempts. */ 1291 public $attemptobjs; 1292 /** @var quiz_access_manager $accessmanager contains various access rules. */ 1293 public $accessmanager; 1294 /** @var bool $canreviewmine whether the current user has the capability to 1295 * review their own attempts. */ 1296 public $canreviewmine; 1297 /** @var bool $canedit whether the current user has the capability to edit the quiz. */ 1298 public $canedit; 1299 /** @var moodle_url $editurl the URL for editing this quiz. */ 1300 public $editurl; 1301 /** @var int $attemptcolumn contains the number of attempts done. */ 1302 public $attemptcolumn; 1303 /** @var int $gradecolumn contains the grades of any attempts. */ 1304 public $gradecolumn; 1305 /** @var int $markcolumn contains the marks of any attempt. */ 1306 public $markcolumn; 1307 /** @var int $overallstats contains all marks for any attempt. */ 1308 public $overallstats; 1309 /** @var string $feedbackcolumn contains any feedback for and attempt. */ 1310 public $feedbackcolumn; 1311 /** @var string $timenow contains a timestamp in string format. */ 1312 public $timenow; 1313 /** @var int $numattempts contains the total number of attempts. */ 1314 public $numattempts; 1315 /** @var float $mygrade contains the user's final grade for a quiz. */ 1316 public $mygrade; 1317 /** @var bool $moreattempts whether this user is allowed more attempts. */ 1318 public $moreattempts; 1319 /** @var int $mygradeoverridden contains an overriden grade. */ 1320 public $mygradeoverridden; 1321 /** @var string $gradebookfeedback contains any feedback for a gradebook. */ 1322 public $gradebookfeedback; 1323 /** @var bool $unfinished contains 1 if an attempt is unfinished. */ 1324 public $unfinished; 1325 /** @var object $lastfinishedattempt the last attempt from the attempts array. */ 1326 public $lastfinishedattempt; 1327 /** @var array $preventmessages of messages telling the user why they can't 1328 * attempt the quiz now. */ 1329 public $preventmessages; 1330 /** @var string $buttontext caption for the start attempt button. If this is null, show no 1331 * button, or if it is '' show a back to the course button. */ 1332 public $buttontext; 1333 /** @var moodle_url $startattempturl URL to start an attempt. */ 1334 public $startattempturl; 1335 /** @var moodleform|null $preflightcheckform confirmation form that must be 1336 * submitted before an attempt is started, if required. */ 1337 public $preflightcheckform; 1338 /** @var moodle_url $startattempturl URL for any Back to the course button. */ 1339 public $backtocourseurl; 1340 /** @var bool $showbacktocourse should we show a back to the course button? */ 1341 public $showbacktocourse; 1342 /** @var bool whether the attempt must take place in a popup window. */ 1343 public $popuprequired; 1344 /** @var array options to use for the popup window, if required. */ 1345 public $popupoptions; 1346 /** @var bool $quizhasquestions whether the quiz has any questions. */ 1347 public $quizhasquestions; 1348 1349 public function __get($field) { 1350 switch ($field) { 1351 case 'startattemptwarning': 1352 debugging('startattemptwarning has been deprecated. It is now always blank.'); 1353 return ''; 1354 1355 default: 1356 debugging('Unknown property ' . $field); 1357 return null; 1358 } 1359 } 1360 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |