[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/question/type/ -> edit_question_form.php (source)

   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   * A base class for question editing forms.
  19   *
  20   * @package    moodlecore
  21   * @subpackage questiontypes
  22   * @copyright  2006 The Open University
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
  24   */
  25  
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  require_once($CFG->libdir.'/formslib.php');
  31  
  32  
  33  abstract class question_wizard_form extends moodleform {
  34      /**
  35       * Add all the hidden form fields used by question/question.php.
  36       */
  37      protected function add_hidden_fields() {
  38          $mform = $this->_form;
  39  
  40          $mform->addElement('hidden', 'id');
  41          $mform->setType('id', PARAM_INT);
  42  
  43          $mform->addElement('hidden', 'inpopup');
  44          $mform->setType('inpopup', PARAM_INT);
  45  
  46          $mform->addElement('hidden', 'cmid');
  47          $mform->setType('cmid', PARAM_INT);
  48  
  49          $mform->addElement('hidden', 'courseid');
  50          $mform->setType('courseid', PARAM_INT);
  51  
  52          $mform->addElement('hidden', 'returnurl');
  53          $mform->setType('returnurl', PARAM_LOCALURL);
  54  
  55          $mform->addElement('hidden', 'scrollpos');
  56          $mform->setType('scrollpos', PARAM_INT);
  57  
  58          $mform->addElement('hidden', 'appendqnumstring');
  59          $mform->setType('appendqnumstring', PARAM_ALPHA);
  60      }
  61  }
  62  
  63  /**
  64   * Form definition base class. This defines the common fields that
  65   * all question types need. Question types should define their own
  66   * class that inherits from this one, and implements the definition_inner()
  67   * method.
  68   *
  69   * @copyright  2006 The Open University
  70   * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
  71   */
  72  abstract class question_edit_form extends question_wizard_form {
  73      const DEFAULT_NUM_HINTS = 2;
  74  
  75      /**
  76       * Question object with options and answers already loaded by get_question_options
  77       * Be careful how you use this it is needed sometimes to set up the structure of the
  78       * form in definition_inner but data is always loaded into the form with set_data.
  79       * @var object
  80       */
  81      protected $question;
  82  
  83      protected $contexts;
  84      protected $category;
  85      protected $categorycontext;
  86  
  87      /** @var object current context */
  88      public $context;
  89      /** @var array html editor options */
  90      public $editoroptions;
  91      /** @var array options to preapre draft area */
  92      public $fileoptions;
  93      /** @var object instance of question type */
  94      public $instance;
  95  
  96      public function __construct($submiturl, $question, $category, $contexts, $formeditable = true) {
  97          global $DB;
  98  
  99          $this->question = $question;
 100          $this->contexts = $contexts;
 101  
 102          $record = $DB->get_record('question_categories',
 103                  array('id' => $question->category), 'contextid');
 104          $this->context = context::instance_by_id($record->contextid);
 105  
 106          $this->editoroptions = array('subdirs' => 1, 'maxfiles' => EDITOR_UNLIMITED_FILES,
 107                  'context' => $this->context);
 108          $this->fileoptions = array('subdirs' => 1, 'maxfiles' => -1, 'maxbytes' => -1);
 109  
 110          $this->category = $category;
 111          $this->categorycontext = context::instance_by_id($category->contextid);
 112  
 113          parent::__construct($submiturl, null, 'post', '', null, $formeditable);
 114      }
 115  
 116      /**
 117       * Build the form definition.
 118       *
 119       * This adds all the form fields that the default question type supports.
 120       * If your question type does not support all these fields, then you can
 121       * override this method and remove the ones you don't want with $mform->removeElement().
 122       */
 123      protected function definition() {
 124          global $COURSE, $CFG, $DB, $PAGE;
 125  
 126          $qtype = $this->qtype();
 127          $langfile = "qtype_{$qtype}";
 128  
 129          $mform = $this->_form;
 130  
 131          // Standard fields at the start of the form.
 132          $mform->addElement('header', 'generalheader', get_string("general", 'form'));
 133  
 134          if (!isset($this->question->id)) {
 135              if (!empty($this->question->formoptions->mustbeusable)) {
 136                  $contexts = $this->contexts->having_add_and_use();
 137              } else {
 138                  $contexts = $this->contexts->having_cap('moodle/question:add');
 139              }
 140  
 141              // Adding question.
 142              $mform->addElement('questioncategory', 'category', get_string('category', 'question'),
 143                      array('contexts' => $contexts));
 144          } else if (!($this->question->formoptions->canmove ||
 145                  $this->question->formoptions->cansaveasnew)) {
 146              // Editing question with no permission to move from category.
 147              $mform->addElement('questioncategory', 'category', get_string('category', 'question'),
 148                      array('contexts' => array($this->categorycontext)));
 149              $mform->addElement('hidden', 'usecurrentcat', 1);
 150              $mform->setType('usecurrentcat', PARAM_BOOL);
 151              $mform->setConstant('usecurrentcat', 1);
 152          } else {
 153              // Editing question with permission to move from category or save as new q.
 154              $currentgrp = array();
 155              $currentgrp[0] = $mform->createElement('questioncategory', 'category',
 156                      get_string('categorycurrent', 'question'),
 157                      array('contexts' => array($this->categorycontext)));
 158              if ($this->question->formoptions->canedit ||
 159                      $this->question->formoptions->cansaveasnew) {
 160                  // Not move only form.
 161                  $currentgrp[1] = $mform->createElement('checkbox', 'usecurrentcat', '',
 162                          get_string('categorycurrentuse', 'question'));
 163                  $mform->setDefault('usecurrentcat', 1);
 164              }
 165              $currentgrp[0]->freeze();
 166              $currentgrp[0]->setPersistantFreeze(false);
 167              $mform->addGroup($currentgrp, 'currentgrp',
 168                      get_string('categorycurrent', 'question'), null, false);
 169  
 170              $mform->addElement('questioncategory', 'categorymoveto',
 171                      get_string('categorymoveto', 'question'),
 172                      array('contexts' => array($this->categorycontext)));
 173              if ($this->question->formoptions->canedit ||
 174                      $this->question->formoptions->cansaveasnew) {
 175                  // Not move only form.
 176                  $mform->disabledIf('categorymoveto', 'usecurrentcat', 'checked');
 177              }
 178          }
 179  
 180          $mform->addElement('text', 'name', get_string('questionname', 'question'),
 181                  array('size' => 50, 'maxlength' => 255));
 182          $mform->setType('name', PARAM_TEXT);
 183          $mform->addRule('name', null, 'required', null, 'client');
 184  
 185          $mform->addElement('editor', 'questiontext', get_string('questiontext', 'question'),
 186                  array('rows' => 15), $this->editoroptions);
 187          $mform->setType('questiontext', PARAM_RAW);
 188          $mform->addRule('questiontext', null, 'required', null, 'client');
 189  
 190          $mform->addElement('text', 'defaultmark', get_string('defaultmark', 'question'),
 191                  array('size' => 7));
 192          $mform->setType('defaultmark', PARAM_FLOAT);
 193          $mform->setDefault('defaultmark', 1);
 194          $mform->addRule('defaultmark', null, 'required', null, 'client');
 195  
 196          $mform->addElement('editor', 'generalfeedback', get_string('generalfeedback', 'question'),
 197                  array('rows' => 10), $this->editoroptions);
 198          $mform->setType('generalfeedback', PARAM_RAW);
 199          $mform->addHelpButton('generalfeedback', 'generalfeedback', 'question');
 200  
 201          // Any questiontype specific fields.
 202          $this->definition_inner($mform);
 203  
 204          if (core_tag_tag::is_enabled('core_question', 'question')) {
 205              $mform->addElement('header', 'tagsheader', get_string('tags'));
 206          }
 207          $mform->addElement('tags', 'tags', get_string('tags'),
 208                  array('itemtype' => 'question', 'component' => 'core_question'));
 209  
 210          if (!empty($this->question->id)) {
 211              $mform->addElement('header', 'createdmodifiedheader',
 212                      get_string('createdmodifiedheader', 'question'));
 213              $a = new stdClass();
 214              if (!empty($this->question->createdby)) {
 215                  $a->time = userdate($this->question->timecreated);
 216                  $a->user = fullname($DB->get_record(
 217                          'user', array('id' => $this->question->createdby)));
 218              } else {
 219                  $a->time = get_string('unknown', 'question');
 220                  $a->user = get_string('unknown', 'question');
 221              }
 222              $mform->addElement('static', 'created', get_string('created', 'question'),
 223                       get_string('byandon', 'question', $a));
 224              if (!empty($this->question->modifiedby)) {
 225                  $a = new stdClass();
 226                  $a->time = userdate($this->question->timemodified);
 227                  $a->user = fullname($DB->get_record(
 228                          'user', array('id' => $this->question->modifiedby)));
 229                  $mform->addElement('static', 'modified', get_string('modified', 'question'),
 230                          get_string('byandon', 'question', $a));
 231              }
 232          }
 233  
 234          $this->add_hidden_fields();
 235  
 236          $mform->addElement('hidden', 'qtype');
 237          $mform->setType('qtype', PARAM_ALPHA);
 238  
 239          $mform->addElement('hidden', 'makecopy');
 240          $mform->setType('makecopy', PARAM_INT);
 241  
 242          $buttonarray = array();
 243          $buttonarray[] = $mform->createElement('submit', 'updatebutton',
 244                               get_string('savechangesandcontinueediting', 'question'));
 245          if ($this->can_preview()) {
 246              $previewlink = $PAGE->get_renderer('core_question')->question_preview_link(
 247                      $this->question->id, $this->context, true);
 248              $buttonarray[] = $mform->createElement('static', 'previewlink', '', $previewlink);
 249          }
 250  
 251          $mform->addGroup($buttonarray, 'updatebuttonar', '', array(' '), false);
 252          $mform->closeHeaderBefore('updatebuttonar');
 253  
 254          $this->add_action_buttons(true, get_string('savechanges'));
 255  
 256          if ((!empty($this->question->id)) && (!($this->question->formoptions->canedit ||
 257                  $this->question->formoptions->cansaveasnew))) {
 258              $mform->hardFreezeAllVisibleExcept(array('categorymoveto', 'buttonar', 'currentgrp'));
 259          }
 260      }
 261  
 262      /**
 263       * Add any question-type specific form fields.
 264       *
 265       * @param object $mform the form being built.
 266       */
 267      protected function definition_inner($mform) {
 268          // By default, do nothing.
 269      }
 270  
 271      /**
 272       * Is the question being edited in a state where it can be previewed?
 273       * @return bool whether to show the preview link.
 274       */
 275      protected function can_preview() {
 276          return empty($this->question->beingcopied) && !empty($this->question->id) &&
 277                  $this->question->formoptions->canedit;
 278      }
 279  
 280      /**
 281       * Get the list of form elements to repeat, one for each answer.
 282       * @param object $mform the form being built.
 283       * @param $label the label to use for each option.
 284       * @param $gradeoptions the possible grades for each answer.
 285       * @param $repeatedoptions reference to array of repeated options to fill
 286       * @param $answersoption reference to return the name of $question->options
 287       *      field holding an array of answers
 288       * @return array of form fields.
 289       */
 290      protected function get_per_answer_fields($mform, $label, $gradeoptions,
 291              &$repeatedoptions, &$answersoption) {
 292          $repeated = array();
 293          $answeroptions = array();
 294          $answeroptions[] = $mform->createElement('text', 'answer',
 295                  $label, array('size' => 40));
 296          $answeroptions[] = $mform->createElement('select', 'fraction',
 297                  get_string('grade'), $gradeoptions);
 298          $repeated[] = $mform->createElement('group', 'answeroptions',
 299                   $label, $answeroptions, null, false);
 300          $repeated[] = $mform->createElement('editor', 'feedback',
 301                  get_string('feedback', 'question'), array('rows' => 5), $this->editoroptions);
 302          $repeatedoptions['answer']['type'] = PARAM_RAW;
 303          $repeatedoptions['fraction']['default'] = 0;
 304          $answersoption = 'answers';
 305          return $repeated;
 306      }
 307  
 308      /**
 309       * Add a set of form fields, obtained from get_per_answer_fields, to the form,
 310       * one for each existing answer, with some blanks for some new ones.
 311       * @param object $mform the form being built.
 312       * @param $label the label to use for each option.
 313       * @param $gradeoptions the possible grades for each answer.
 314       * @param $minoptions the minimum number of answer blanks to display.
 315       *      Default QUESTION_NUMANS_START.
 316       * @param $addoptions the number of answer blanks to add. Default QUESTION_NUMANS_ADD.
 317       */
 318      protected function add_per_answer_fields(&$mform, $label, $gradeoptions,
 319              $minoptions = QUESTION_NUMANS_START, $addoptions = QUESTION_NUMANS_ADD) {
 320          $mform->addElement('header', 'answerhdr',
 321                      get_string('answers', 'question'), '');
 322          $mform->setExpanded('answerhdr', 1);
 323          $answersoption = '';
 324          $repeatedoptions = array();
 325          $repeated = $this->get_per_answer_fields($mform, $label, $gradeoptions,
 326                  $repeatedoptions, $answersoption);
 327  
 328          if (isset($this->question->options)) {
 329              $repeatsatstart = count($this->question->options->$answersoption);
 330          } else {
 331              $repeatsatstart = $minoptions;
 332          }
 333  
 334          $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions,
 335                  'noanswers', 'addanswers', $addoptions,
 336                  $this->get_more_choices_string(), true);
 337      }
 338  
 339      /**
 340       * Language string to use for 'Add {no} more {whatever we call answers}'.
 341       */
 342      protected function get_more_choices_string() {
 343          return get_string('addmorechoiceblanks', 'question');
 344      }
 345  
 346      protected function add_combined_feedback_fields($withshownumpartscorrect = false) {
 347          $mform = $this->_form;
 348  
 349          $mform->addElement('header', 'combinedfeedbackhdr',
 350                  get_string('combinedfeedback', 'question'));
 351  
 352          $fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback');
 353          foreach ($fields as $feedbackname) {
 354              $element = $mform->addElement('editor', $feedbackname,
 355                                  get_string($feedbackname, 'question'),
 356                                  array('rows' => 5), $this->editoroptions);
 357              $mform->setType($feedbackname, PARAM_RAW);
 358              // Using setValue() as setDefault() does not work for the editor class.
 359              $element->setValue(array('text' => get_string($feedbackname.'default', 'question')));
 360  
 361              if ($withshownumpartscorrect && $feedbackname == 'partiallycorrectfeedback') {
 362                  $mform->addElement('advcheckbox', 'shownumcorrect',
 363                          get_string('options', 'question'),
 364                          get_string('shownumpartscorrectwhenfinished', 'question'));
 365                  $mform->setDefault('shownumcorrect', true);
 366              }
 367          }
 368      }
 369  
 370      /**
 371       * Create the form elements required by one hint.
 372       * @param string $withclearwrong whether this quesiton type uses the 'Clear wrong' option on hints.
 373       * @param string $withshownumpartscorrect whether this quesiton type uses the 'Show num parts correct' option on hints.
 374       * @return array form field elements for one hint.
 375       */
 376      protected function get_hint_fields($withclearwrong = false, $withshownumpartscorrect = false) {
 377          $mform = $this->_form;
 378  
 379          $repeatedoptions = array();
 380          $repeated = array();
 381          $repeated[] = $mform->createElement('editor', 'hint', get_string('hintn', 'question'),
 382                  array('rows' => 5), $this->editoroptions);
 383          $repeatedoptions['hint']['type'] = PARAM_RAW;
 384  
 385          $optionelements = array();
 386          if ($withclearwrong) {
 387              $optionelements[] = $mform->createElement('advcheckbox', 'hintclearwrong',
 388                      get_string('options', 'question'), get_string('clearwrongparts', 'question'));
 389          }
 390          if ($withshownumpartscorrect) {
 391              $optionelements[] = $mform->createElement('advcheckbox', 'hintshownumcorrect', '',
 392                      get_string('shownumpartscorrect', 'question'));
 393          }
 394  
 395          if (count($optionelements)) {
 396              $repeated[] = $mform->createElement('group', 'hintoptions',
 397                   get_string('hintnoptions', 'question'), $optionelements, null, false);
 398          }
 399  
 400          return array($repeated, $repeatedoptions);
 401      }
 402  
 403      protected function add_interactive_settings($withclearwrong = false,
 404              $withshownumpartscorrect = false) {
 405          $mform = $this->_form;
 406  
 407          $mform->addElement('header', 'multitriesheader',
 408                  get_string('settingsformultipletries', 'question'));
 409  
 410          $penalties = array(
 411              1.0000000,
 412              0.5000000,
 413              0.3333333,
 414              0.2500000,
 415              0.2000000,
 416              0.1000000,
 417              0.0000000
 418          );
 419          if (!empty($this->question->penalty) && !in_array($this->question->penalty, $penalties)) {
 420              $penalties[] = $this->question->penalty;
 421              sort($penalties);
 422          }
 423          $penaltyoptions = array();
 424          foreach ($penalties as $penalty) {
 425              $penaltyoptions["{$penalty}"] = (100 * $penalty) . '%';
 426          }
 427          $mform->addElement('select', 'penalty',
 428                  get_string('penaltyforeachincorrecttry', 'question'), $penaltyoptions);
 429          $mform->addHelpButton('penalty', 'penaltyforeachincorrecttry', 'question');
 430          $mform->setDefault('penalty', 0.3333333);
 431  
 432          if (isset($this->question->hints)) {
 433              $counthints = count($this->question->hints);
 434          } else {
 435              $counthints = 0;
 436          }
 437  
 438          if ($this->question->formoptions->repeatelements) {
 439              $repeatsatstart = max(self::DEFAULT_NUM_HINTS, $counthints);
 440          } else {
 441              $repeatsatstart = $counthints;
 442          }
 443  
 444          list($repeated, $repeatedoptions) = $this->get_hint_fields(
 445                  $withclearwrong, $withshownumpartscorrect);
 446          $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions,
 447                  'numhints', 'addhint', 1, get_string('addanotherhint', 'question'), true);
 448      }
 449  
 450      public function set_data($question) {
 451          question_bank::get_qtype($question->qtype)->set_default_options($question);
 452  
 453          // Prepare question text.
 454          $draftid = file_get_submitted_draft_itemid('questiontext');
 455  
 456          if (!empty($question->questiontext)) {
 457              $questiontext = $question->questiontext;
 458          } else {
 459              $questiontext = $this->_form->getElement('questiontext')->getValue();
 460              $questiontext = $questiontext['text'];
 461          }
 462          $questiontext = file_prepare_draft_area($draftid, $this->context->id,
 463                  'question', 'questiontext', empty($question->id) ? null : (int) $question->id,
 464                  $this->fileoptions, $questiontext);
 465  
 466          $question->questiontext = array();
 467          $question->questiontext['text'] = $questiontext;
 468          $question->questiontext['format'] = empty($question->questiontextformat) ?
 469                  editors_get_preferred_format() : $question->questiontextformat;
 470          $question->questiontext['itemid'] = $draftid;
 471  
 472          // Prepare general feedback.
 473          $draftid = file_get_submitted_draft_itemid('generalfeedback');
 474  
 475          if (empty($question->generalfeedback)) {
 476              $generalfeedback = $this->_form->getElement('generalfeedback')->getValue();
 477              $question->generalfeedback = $generalfeedback['text'];
 478          }
 479  
 480          $feedback = file_prepare_draft_area($draftid, $this->context->id,
 481                  'question', 'generalfeedback', empty($question->id) ? null : (int) $question->id,
 482                  $this->fileoptions, $question->generalfeedback);
 483          $question->generalfeedback = array();
 484          $question->generalfeedback['text'] = $feedback;
 485          $question->generalfeedback['format'] = empty($question->generalfeedbackformat) ?
 486                  editors_get_preferred_format() : $question->generalfeedbackformat;
 487          $question->generalfeedback['itemid'] = $draftid;
 488  
 489          // Remove unnecessary trailing 0s form grade fields.
 490          if (isset($question->defaultgrade)) {
 491              $question->defaultgrade = 0 + $question->defaultgrade;
 492          }
 493          if (isset($question->penalty)) {
 494              $question->penalty = 0 + $question->penalty;
 495          }
 496  
 497          // Set any options.
 498          $extraquestionfields = question_bank::get_qtype($question->qtype)->extra_question_fields();
 499          if (is_array($extraquestionfields) && !empty($question->options)) {
 500              array_shift($extraquestionfields);
 501              foreach ($extraquestionfields as $field) {
 502                  if (property_exists($question->options, $field)) {
 503                      $question->$field = $question->options->$field;
 504                  }
 505              }
 506          }
 507  
 508          // Subclass adds data_preprocessing code here.
 509          $question = $this->data_preprocessing($question);
 510  
 511          parent::set_data($question);
 512      }
 513  
 514      /**
 515       * Perform an preprocessing needed on the data passed to {@link set_data()}
 516       * before it is used to initialise the form.
 517       * @param object $question the data being passed to the form.
 518       * @return object $question the modified data.
 519       */
 520      protected function data_preprocessing($question) {
 521          return $question;
 522      }
 523  
 524      /**
 525       * Perform the necessary preprocessing for the fields added by
 526       * {@link add_per_answer_fields()}.
 527       * @param object $question the data being passed to the form.
 528       * @return object $question the modified data.
 529       */
 530      protected function data_preprocessing_answers($question, $withanswerfiles = false) {
 531          if (empty($question->options->answers)) {
 532              return $question;
 533          }
 534  
 535          $key = 0;
 536          foreach ($question->options->answers as $answer) {
 537              if ($withanswerfiles) {
 538                  // Prepare the feedback editor to display files in draft area.
 539                  $draftitemid = file_get_submitted_draft_itemid('answer['.$key.']');
 540                  $question->answer[$key]['text'] = file_prepare_draft_area(
 541                      $draftitemid,          // Draftid
 542                      $this->context->id,    // context
 543                      'question',            // component
 544                      'answer',              // filarea
 545                      !empty($answer->id) ? (int) $answer->id : null, // itemid
 546                      $this->fileoptions,    // options
 547                      $answer->answer        // text.
 548                  );
 549                  $question->answer[$key]['itemid'] = $draftitemid;
 550                  $question->answer[$key]['format'] = $answer->answerformat;
 551              } else {
 552                  $question->answer[$key] = $answer->answer;
 553              }
 554  
 555              $question->fraction[$key] = 0 + $answer->fraction;
 556              $question->feedback[$key] = array();
 557  
 558              // Evil hack alert. Formslib can store defaults in two ways for
 559              // repeat elements:
 560              //   ->_defaultValues['fraction[0]'] and
 561              //   ->_defaultValues['fraction'][0].
 562              // The $repeatedoptions['fraction']['default'] = 0 bit above means
 563              // that ->_defaultValues['fraction[0]'] has already been set, but we
 564              // are using object notation here, so we will be setting
 565              // ->_defaultValues['fraction'][0]. That does not work, so we have
 566              // to unset ->_defaultValues['fraction[0]'].
 567              unset($this->_form->_defaultValues["fraction[{$key}]"]);
 568  
 569              // Prepare the feedback editor to display files in draft area.
 570              $draftitemid = file_get_submitted_draft_itemid('feedback['.$key.']');
 571              $question->feedback[$key]['text'] = file_prepare_draft_area(
 572                  $draftitemid,          // Draftid
 573                  $this->context->id,    // context
 574                  'question',            // component
 575                  'answerfeedback',      // filarea
 576                  !empty($answer->id) ? (int) $answer->id : null, // itemid
 577                  $this->fileoptions,    // options
 578                  $answer->feedback      // text.
 579              );
 580              $question->feedback[$key]['itemid'] = $draftitemid;
 581              $question->feedback[$key]['format'] = $answer->feedbackformat;
 582              $key++;
 583          }
 584  
 585          // Now process extra answer fields.
 586          $extraanswerfields = question_bank::get_qtype($question->qtype)->extra_answer_fields();
 587          if (is_array($extraanswerfields)) {
 588              // Omit table name.
 589              array_shift($extraanswerfields);
 590              $question = $this->data_preprocessing_extra_answer_fields($question, $extraanswerfields);
 591          }
 592  
 593          return $question;
 594      }
 595  
 596      /**
 597       * Perform the necessary preprocessing for the extra answer fields.
 598       *
 599       * Questions that do something not trivial when editing extra answer fields
 600       * will want to override this.
 601       * @param object $question the data being passed to the form.
 602       * @param array $extraanswerfields extra answer fields (without table name).
 603       * @return object $question the modified data.
 604       */
 605      protected function data_preprocessing_extra_answer_fields($question, $extraanswerfields) {
 606          // Setting $question->$field[$key] won't work in PHP, so we need set an array of answer values to $question->$field.
 607          // As we may have several extra fields with data for several answers in each, we use an array of arrays.
 608          // Index in $extrafieldsdata is an extra answer field name, value - array of it's data for each answer.
 609          $extrafieldsdata = array();
 610          // First, prepare an array if empty arrays for each extra answer fields data.
 611          foreach ($extraanswerfields as $field) {
 612              $extrafieldsdata[$field] = array();
 613          }
 614  
 615          // Fill arrays with data from $question->options->answers.
 616          $key = 0;
 617          foreach ($question->options->answers as $answer) {
 618              foreach ($extraanswerfields as $field) {
 619                  // See hack comment in {@link data_preprocessing_answers()}.
 620                  unset($this->_form->_defaultValues["{$field}[{$key}]"]);
 621                  $extrafieldsdata[$field][$key] = $this->data_preprocessing_extra_answer_field($answer, $field);
 622              }
 623              $key++;
 624          }
 625  
 626          // Set this data in the $question object.
 627          foreach ($extraanswerfields as $field) {
 628              $question->$field = $extrafieldsdata[$field];
 629          }
 630          return $question;
 631      }
 632  
 633      /**
 634       * Perfmorm preprocessing for particular extra answer field.
 635       *
 636       * Questions with non-trivial DB - form element relationship will
 637       * want to override this.
 638       * @param object $answer an answer object to get extra field from.
 639       * @param string $field extra answer field name.
 640       * @return field value to be set to the form.
 641       */
 642      protected function data_preprocessing_extra_answer_field($answer, $field) {
 643          return $answer->$field;
 644      }
 645  
 646      /**
 647       * Perform the necessary preprocessing for the fields added by
 648       * {@link add_combined_feedback_fields()}.
 649       * @param object $question the data being passed to the form.
 650       * @return object $question the modified data.
 651       */
 652      protected function data_preprocessing_combined_feedback($question,
 653              $withshownumcorrect = false) {
 654          if (empty($question->options)) {
 655              return $question;
 656          }
 657  
 658          $fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback');
 659          foreach ($fields as $feedbackname) {
 660              $draftid = file_get_submitted_draft_itemid($feedbackname);
 661              $feedback = array();
 662              $feedback['text'] = file_prepare_draft_area(
 663                  $draftid,              // Draftid
 664                  $this->context->id,    // context
 665                  'question',            // component
 666                  $feedbackname,         // filarea
 667                  !empty($question->id) ? (int) $question->id : null, // itemid
 668                  $this->fileoptions,    // options
 669                  $question->options->$feedbackname // text.
 670              );
 671              $feedbackformat = $feedbackname . 'format';
 672              $feedback['format'] = $question->options->$feedbackformat;
 673              $feedback['itemid'] = $draftid;
 674  
 675              $question->$feedbackname = $feedback;
 676          }
 677  
 678          if ($withshownumcorrect) {
 679              $question->shownumcorrect = $question->options->shownumcorrect;
 680          }
 681  
 682          return $question;
 683      }
 684  
 685      /**
 686       * Perform the necessary preprocessing for the hint fields.
 687       * @param object $question the data being passed to the form.
 688       * @return object $question the modified data.
 689       */
 690      protected function data_preprocessing_hints($question, $withclearwrong = false,
 691              $withshownumpartscorrect = false) {
 692          if (empty($question->hints)) {
 693              return $question;
 694          }
 695  
 696          $key = 0;
 697          foreach ($question->hints as $hint) {
 698              $question->hint[$key] = array();
 699  
 700              // Prepare feedback editor to display files in draft area.
 701              $draftitemid = file_get_submitted_draft_itemid('hint['.$key.']');
 702              $question->hint[$key]['text'] = file_prepare_draft_area(
 703                  $draftitemid,          // Draftid
 704                  $this->context->id,    // context
 705                  'question',            // component
 706                  'hint',                // filarea
 707                  !empty($hint->id) ? (int) $hint->id : null, // itemid
 708                  $this->fileoptions,    // options
 709                  $hint->hint            // text.
 710              );
 711              $question->hint[$key]['itemid'] = $draftitemid;
 712              $question->hint[$key]['format'] = $hint->hintformat;
 713              $key++;
 714  
 715              if ($withclearwrong) {
 716                  $question->hintclearwrong[] = $hint->clearwrong;
 717              }
 718              if ($withshownumpartscorrect) {
 719                  $question->hintshownumcorrect[] = $hint->shownumcorrect;
 720              }
 721          }
 722  
 723          return $question;
 724      }
 725  
 726      public function validation($fromform, $files) {
 727          $errors = parent::validation($fromform, $files);
 728          if (empty($fromform['makecopy']) && isset($this->question->id)
 729                  && ($this->question->formoptions->canedit ||
 730                          $this->question->formoptions->cansaveasnew)
 731                  && empty($fromform['usecurrentcat']) && !$this->question->formoptions->canmove) {
 732              $errors['currentgrp'] = get_string('nopermissionmove', 'question');
 733          }
 734  
 735          // Default mark.
 736          if (array_key_exists('defaultmark', $fromform) && $fromform['defaultmark'] < 0) {
 737              $errors['defaultmark'] = get_string('defaultmarkmustbepositive', 'question');
 738          }
 739  
 740          return $errors;
 741      }
 742  
 743      /**
 744       * Override this in the subclass to question type name.
 745       * @return the question type name, should be the same as the name() method
 746       *      in the question type class.
 747       */
 748      public abstract function qtype();
 749  
 750      /**
 751       * Returns an array of editor options with collapsed options turned off.
 752       * @deprecated since 2.6
 753       * @return array
 754       */
 755      protected function get_non_collabsible_editor_options() {
 756          debugging('get_non_collabsible_editor_options() is deprecated, use $this->editoroptions instead.', DEBUG_DEVELOPER);
 757          return $this->editoroptions;
 758      }
 759  
 760  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1