Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
   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 base classes for question types.
  19   *
  20   * @package    moodlecore
  21   * @subpackage questiontypes
  22   * @copyright  2009 The Open University
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  
  30  /**
  31   * Renderer base classes for question types.
  32   *
  33   * @copyright  2009 The Open University
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  abstract class qtype_renderer extends plugin_renderer_base {
  37      /**
  38       * Generate the display of the formulation part of the question. This is the
  39       * area that contains the quetsion text, and the controls for students to
  40       * input their answers. Some question types also embed bits of feedback, for
  41       * example ticks and crosses, in this area.
  42       *
  43       * @param question_attempt $qa the question attempt to display.
  44       * @param question_display_options $options controls what should and should not be displayed.
  45       * @return string HTML fragment.
  46       */
  47      public function formulation_and_controls(question_attempt $qa,
  48              question_display_options $options) {
  49          return $qa->get_question()->format_questiontext($qa);
  50      }
  51  
  52      /**
  53       * In the question output there are some class="accesshide" headers to help
  54       * screen-readers. This method returns the text to use for the heading above
  55       * the formulation_and_controls section.
  56       * @return string to use as the heading.
  57       */
  58      public function formulation_heading() {
  59          return get_string('questiontext', 'question');
  60      }
  61  
  62      /**
  63       * Output hidden form fields to clear any wrong parts of the student's response.
  64       *
  65       * This method will only be called if the question is in read-only mode.
  66       * @param question_attempt $qa the question attempt to display.
  67       * @return string HTML fragment.
  68       */
  69      public function clear_wrong(question_attempt $qa) {
  70          $response = $qa->get_last_qt_data();
  71          if (!$response) {
  72              return '';
  73          }
  74          $cleanresponse = $qa->get_question()->clear_wrong_from_response($response);
  75          $output = '';
  76          foreach ($cleanresponse as $name => $value) {
  77              $attr = array(
  78                  'type' => 'hidden',
  79                  'name' => $qa->get_qt_field_name($name),
  80                  'value' => s($value),
  81              );
  82              $output .= html_writer::empty_tag('input', $attr);
  83          }
  84          return $output;
  85      }
  86  
  87      /**
  88       * Generate the display of the outcome part of the question. This is the
  89       * area that contains the various forms of feedback. This function generates
  90       * the content of this area belonging to the question type.
  91       *
  92       * Subclasses will normally want to override the more specific methods
  93       * {specific_feedback()}, {general_feedback()} and {correct_response()}
  94       * that this method calls.
  95       *
  96       * @param question_attempt $qa the question attempt to display.
  97       * @param question_display_options $options controls what should and should not be displayed.
  98       * @return string HTML fragment.
  99       */
 100      public function feedback(question_attempt $qa, question_display_options $options) {
 101          $output = '';
 102          $hint = null;
 103  
 104          if ($options->feedback) {
 105              $output .= html_writer::nonempty_tag('div', $this->specific_feedback($qa),
 106                      array('class' => 'specificfeedback'));
 107              $hint = $qa->get_applicable_hint();
 108          }
 109  
 110          if ($options->numpartscorrect) {
 111              $output .= html_writer::nonempty_tag('div', $this->num_parts_correct($qa),
 112                      array('class' => 'numpartscorrect'));
 113          }
 114  
 115          if ($hint) {
 116              $output .= $this->hint($qa, $hint);
 117          }
 118  
 119          if ($options->generalfeedback) {
 120              $output .= html_writer::nonempty_tag('div', $this->general_feedback($qa),
 121                      array('class' => 'generalfeedback'));
 122          }
 123  
 124          if ($options->rightanswer) {
 125              $output .= html_writer::nonempty_tag('div', $this->correct_response($qa),
 126                      array('class' => 'rightanswer'));
 127          }
 128  
 129          return $output;
 130      }
 131  
 132      /**
 133       * Generate the specific feedback. This is feedback that varies according to
 134       * the response the student gave.
 135       * @param question_attempt $qa the question attempt to display.
 136       * @return string HTML fragment.
 137       */
 138      protected function specific_feedback(question_attempt $qa) {
 139          return '';
 140      }
 141  
 142      /**
 143       * Gereate a brief statement of how many sub-parts of this question the
 144       * student got right.
 145       * @param question_attempt $qa the question attempt to display.
 146       * @return string HTML fragment.
 147       */
 148      protected function num_parts_correct(question_attempt $qa) {
 149          $a = new stdClass();
 150          list($a->num, $a->outof) = $qa->get_question()->get_num_parts_right(
 151                  $qa->get_last_qt_data());
 152          if (is_null($a->outof)) {
 153              return '';
 154          } else {
 155              return get_string('yougotnright', 'question', $a);
 156          }
 157      }
 158  
 159      /**
 160       * Gereate the specific feedback. This is feedback that varies according to
 161       * the response the student gave.
 162       * @param question_attempt $qa the question attempt to display.
 163       * @return string HTML fragment.
 164       */
 165      protected function hint(question_attempt $qa, question_hint $hint) {
 166          return html_writer::nonempty_tag('div',
 167                  $qa->get_question()->format_hint($hint, $qa), array('class' => 'hint'));
 168      }
 169  
 170      /**
 171       * Gereate the general feedback. This is feedback is shown ot all students.
 172       *
 173       * @param question_attempt $qa the question attempt to display.
 174       * @return string HTML fragment.
 175       */
 176      protected function general_feedback(question_attempt $qa) {
 177          return $qa->get_question()->format_generalfeedback($qa);
 178      }
 179  
 180      /**
 181       * Gereate an automatic description of the correct response to this question.
 182       * Not all question types can do this. If it is not possible, this method
 183       * should just return an empty string.
 184       *
 185       * @param question_attempt $qa the question attempt to display.
 186       * @return string HTML fragment.
 187       */
 188      protected function correct_response(question_attempt $qa) {
 189          return '';
 190      }
 191  
 192      /**
 193       * Display any extra question-type specific content that should be visible
 194       * when grading, if appropriate.
 195       *
 196       * @param question_attempt $qa a question attempt.
 197       * @param question_display_options $options controls what should and should not be displayed.
 198       * @return string HTML fragment.
 199       */
 200      public function manual_comment(question_attempt $qa, question_display_options $options) {
 201          return '';
 202      }
 203  
 204      /**
 205       * Return any HTML that needs to be included in the page's <head> when this
 206       * question is used.
 207       * @param $qa the question attempt that will be displayed on the page.
 208       * @return string HTML fragment.
 209       */
 210      public function head_code(question_attempt $qa) {
 211          // This method is used by the Opaque question type. The remote question
 212          // engine can send back arbitrary CSS that we have to link to in the
 213          // page header. If it was not for that, we might be able to eliminate
 214          // this method and load the required CSS and JS some other way.
 215          $qa->get_question()->qtype->find_standard_scripts();
 216      }
 217  
 218      protected function feedback_class($fraction) {
 219          return question_state::graded_state_for_fraction($fraction)->get_feedback_class();
 220      }
 221  
 222      /**
 223       * Return an appropriate icon (green tick, red cross, etc.) for a grade.
 224       * @param float $fraction grade on a scale 0..1.
 225       * @param bool $selected whether to show a big or small icon. (Deprecated)
 226       * @return string html fragment.
 227       */
 228      protected function feedback_image($fraction, $selected = true) {
 229          $feedbackclass = question_state::graded_state_for_fraction($fraction)->get_feedback_class();
 230  
 231          return $this->output->pix_icon('i/grade_' . $feedbackclass, get_string($feedbackclass, 'question'));
 232      }
 233  }
 234  
 235  /**
 236   * Renderer base classes for question types.
 237   *
 238   * @copyright  2010 The Open University
 239   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 240   */
 241  abstract class qtype_with_combined_feedback_renderer extends qtype_renderer {
 242      protected function combined_feedback(question_attempt $qa) {
 243          $question = $qa->get_question();
 244  
 245          $state = $qa->get_state();
 246  
 247          if (!$state->is_finished()) {
 248              $response = $qa->get_last_qt_data();
 249              if (!$qa->get_question()->is_gradable_response($response)) {
 250                  return '';
 251              }
 252              list($notused, $state) = $qa->get_question()->grade_response($response);
 253          }
 254  
 255          $feedback = '';
 256          $field = $state->get_feedback_class() . 'feedback';
 257          $format = $state->get_feedback_class() . 'feedbackformat';
 258          if ($question->$field) {
 259              $feedback .= $question->format_text($question->$field, $question->$format,
 260                      $qa, 'question', $field, $question->id);
 261          }
 262  
 263          return $feedback;
 264      }
 265  }