Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 401 and 402] [Versions 401 and 403]

   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   * mod_feedback data generator.
  19   *
  20   * @package    mod_feedback
  21   * @category   test
  22   * @copyright  2013 Ankit Agarwal
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * mod_feedback data generator class.
  30   *
  31   * @package    mod_feedback
  32   * @category   test
  33   * @copyright  2013 Ankit Agarwal
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class mod_feedback_generator extends testing_module_generator {
  37  
  38      public function create_instance($record = null, array $options = null) {
  39          global $CFG;
  40          require_once($CFG->dirroot.'/mod/feedback/lib.php');
  41          $record = (object)(array)$record;
  42  
  43          if (!isset($record->anonymous)) {
  44              $record->anonymous = FEEDBACK_ANONYMOUS_YES;
  45          }
  46          if (!isset($record->email_notification)) {
  47              $record->email_notification = 0;
  48          }
  49          if (!isset($record->multiple_submit)) {
  50              $record->multiple_submit = 0;
  51          }
  52          if (!isset($record->autonumbering)) {
  53              $record->autonumbering = 0;
  54          }
  55          if (!isset($record->site_after_submit)) {
  56              $record->site_after_submit = '';
  57          }
  58          if (!isset($record->page_after_submit)) {
  59              $record->page_after_submit = 'This is page after submit';
  60          }
  61          if (!isset($record->page_after_submitformat)) {
  62              $record->page_after_submitformat = FORMAT_MOODLE;
  63          }
  64          if (!isset($record->publish_stats)) {
  65              $record->publish_stats = 0;
  66          }
  67          if (!isset($record->timeopen)) {
  68              $record->timeopen = 0;
  69          }
  70          if (!isset($record->timeclose)) {
  71              $record->timeclose = 0;
  72          }
  73          if (!isset($record->timemodified)) {
  74              $record->timemodified = time();
  75          }
  76          if (!isset($record->completionsubmit)) {
  77              $record->completionsubmit = 0;
  78          }
  79  
  80          // Hack to bypass draft processing of feedback_add_instance.
  81          $record->page_after_submit_editor['itemid'] = false;
  82  
  83          return parent::create_instance($record, (array)$options);
  84      }
  85  
  86      /**
  87       * Create question.
  88       *
  89       * @param array $data Question data
  90       * @return mixed Question instance
  91       */
  92      public function create_question(array $data) {
  93          global $DB;
  94  
  95          $questiontype = $data['questiontype'] ?? 'textfield';
  96          $cm = get_coursemodule_from_id('feedback', $data['cmid']);
  97          $feedback = $DB->get_record('feedback', ['id' => $cm->instance]);
  98  
  99          unset($data['questiontype']);
 100          unset($data['cmid']);
 101  
 102          if (isset($data['values'])) {
 103              $data['values'] = $this->format_item_values($questiontype, $data['values']);
 104          }
 105  
 106          return call_user_func([$this, "create_item_{$questiontype}"], $feedback, $data);
 107      }
 108  
 109      /**
 110       * Create response.
 111       *
 112       * @param array $data Response data.
 113       * @return stdClass feedback_completed response instance.
 114       */
 115      public function create_response(array $data): stdClass {
 116          global $DB;
 117  
 118          $userid = $data['userid'];
 119          $responsenumber = null;
 120          $cm = get_coursemodule_from_id('feedback', $data['cmid']);
 121          $feedback = $DB->get_record('feedback', ['id' => $cm->instance]);
 122          $answers = [];
 123  
 124          if (isset($data['responsenumber']) && trim($data['responsenumber']) !== '') {
 125              $responsenumber = $data['responsenumber'];
 126          }
 127  
 128          if (isset($data['anonymous']) && trim($data['anonymous']) !== '') {
 129              $anonymous = filter_var(trim($data['anonymous']), FILTER_VALIDATE_BOOLEAN);
 130              $feedback->anonymous = $anonymous ? FEEDBACK_ANONYMOUS_YES : FEEDBACK_ANONYMOUS_NO;
 131          }
 132  
 133          unset($data['cmid']);
 134          unset($data['userid']);
 135          unset($data['anonymous']);
 136          unset($data['responsenumber']);
 137  
 138          foreach ($data as $question => $response) {
 139              $item = $DB->get_record('feedback_item', ['name' => trim($question)], '*', MUST_EXIST);
 140  
 141              $answers["{$item->typ}_{$item->id}"] = $this->get_item_response_value($item, $response);
 142          }
 143  
 144          $feedbackcompletion = new mod_feedback_completion(
 145              $feedback,
 146              $cm,
 147              $cm->course,
 148              false,
 149              null,
 150              $feedback->anonymous === FEEDBACK_ANONYMOUS_YES ? null : $userid,
 151              $userid
 152          );
 153  
 154          if (!$feedbackcompletion->can_complete()) {
 155              throw new coding_exception("User {$userid} cannot complete this feedback activity.");
 156          }
 157  
 158          if (!$feedbackcompletion->is_open()) {
 159              throw new coding_exception("This activity is not open.");
 160          }
 161  
 162          $feedbackcompletion->set_module_viewed();
 163          $feedbackcompletion->save_response_tmp((object) $answers);
 164          $feedbackcompletion->save_response();
 165          $completed = $feedbackcompletion->get_completed();
 166  
 167          if (!is_null($responsenumber)) {
 168              $DB->update_record('feedback_completed', [
 169                  'id' => $completed->id,
 170                  'random_response' => $responsenumber,
 171              ]);
 172          }
 173  
 174          return $completed;
 175      }
 176  
 177      /**
 178       * Create info question item.
 179       *
 180       * @param object $feedback feedback record
 181       * @param array $record (optional) to override default values
 182       * @return int
 183       */
 184      public function create_item_info($feedback, $record = array()) {
 185          global $DB, $CFG;
 186  
 187          require_once($CFG->dirroot.'/mod/feedback/lib.php');
 188  
 189          $itemobj = feedback_get_item_class('info');
 190          $position = $DB->count_records('feedback_item', array('feedback' => $feedback->id)) + 1;
 191  
 192          $record = (array)$record + array(
 193              'id' => 0,
 194              'feedback' => $feedback->id,
 195              'template' => 0,
 196              'name' => 'Feedback question item ' . $position,
 197              'label' => 'Feedback label ' . $position,
 198              'presentation' => $itemobj::MODE_COURSE,
 199              'typ' => 'info',
 200              'hasvalue' => 0,
 201              'position' => $position,
 202              'required' => 0,
 203              'dependitem' => 0,
 204              'dependvalue' => '',
 205              'options' => '',
 206          );
 207  
 208          $itemobj->set_data((object) $record);
 209          return $itemobj->save_item();
 210      }
 211  
 212      /**
 213       * Create label question item.
 214       *
 215       * @param object $feedback feedback record
 216       * @param array $record (optional) to override default values
 217       * @return int
 218       */
 219      public function create_item_label($feedback, $record = array()) {
 220          global $DB, $CFG;
 221  
 222          require_once($CFG->dirroot.'/mod/feedback/lib.php');
 223  
 224          $itemobj = feedback_get_item_class('label');
 225          $position = $DB->count_records('feedback_item', array('feedback' => $feedback->id)) + 1;
 226  
 227          $record = (array)$record + array(
 228              'id' => 0,
 229              'feedback' => $feedback->id,
 230              'template' => 0,
 231              'name' => 'label',
 232              'label' => '',
 233              'presentation' => '',
 234              'typ' => 'label',
 235              'hasvalue' => 0,
 236              'position' => $position,
 237              'required' => 0,
 238              'dependitem' => 0,
 239              'dependvalue' => '',
 240              'options' => '',
 241          );
 242  
 243          if (!isset($record['presentation_editor'])) {
 244              $record['presentation_editor'] = array(
 245                  'text' => "The label $position text goes here",
 246                  'format' => FORMAT_HTML,
 247                  'itemid' => 0
 248              );
 249          }
 250  
 251          $itemobj->set_data((object) $record);
 252          return $itemobj->save_item();
 253      }
 254  
 255      /**
 256       * Create multichoice question item.
 257       *
 258       * @param object $feedback feedback record
 259       * @param array $record (optional) to override default values
 260       * @return int
 261       */
 262      public function create_item_multichoice($feedback, $record = array()) {
 263          global $DB, $CFG;
 264  
 265          require_once($CFG->dirroot.'/mod/feedback/lib.php');
 266  
 267          $itemobj = feedback_get_item_class('multichoice');
 268          $position = $DB->count_records('feedback_item', array('feedback' => $feedback->id)) + 1;
 269  
 270          $record = (array)$record + array(
 271              'id' => 0,
 272              'feedback' => $feedback->id,
 273              'template' => 0,
 274              'name' => 'Feedback question item ' . $position,
 275              'label' => 'Feedback label ' . $position,
 276              'presentation' => '',
 277              'typ' => 'multichoice',
 278              'hasvalue' => 0,
 279              'position' => $position,
 280              'required' => 0,
 281              'dependitem' => 0,
 282              'dependvalue' => '',
 283              'options' => '',
 284              'subtype' => 'r',
 285              'horizontal' => 0,
 286              'hidenoselect' => 1,
 287              'ignoreempty' => 0,
 288              'values' => "a\nb\nc\nd\ne"
 289          );
 290  
 291          $presentation = str_replace("\n", FEEDBACK_MULTICHOICE_LINE_SEP, trim($record['values']));
 292  
 293          if ($record['horizontal'] == 1 AND $record['subtype'] != 'd') {
 294              $presentation .= FEEDBACK_MULTICHOICE_ADJUST_SEP.'1';
 295          }
 296          $record['presentation'] = $record['subtype'].FEEDBACK_MULTICHOICE_TYPE_SEP.$presentation;
 297  
 298          $itemobj->set_data((object) $record);
 299          return $itemobj->save_item();
 300      }
 301  
 302      /**
 303       * Create multichoicerated question item.
 304       *
 305       * @param object $feedback feedback record
 306       * @param array $record (optional) to override default values
 307       * @return int
 308       */
 309      public function create_item_multichoicerated($feedback, $record = array()) {
 310          global $DB, $CFG;
 311  
 312          require_once($CFG->dirroot.'/mod/feedback/lib.php');
 313  
 314          $itemobj = feedback_get_item_class('multichoicerated');
 315          $position = $DB->count_records('feedback_item', array('feedback' => $feedback->id)) + 1;
 316  
 317          $record = (array)$record + array(
 318              'id' => 0,
 319              'feedback' => $feedback->id,
 320              'template' => 0,
 321              'name' => 'Feedback question item ' . $position,
 322              'label' => 'Feedback label ' . $position,
 323              'presentation' => '',
 324              'typ' => 'multichoicerated',
 325              'hasvalue' => 0,
 326              'position' => $position,
 327              'required' => 0,
 328              'dependitem' => 0,
 329              'dependvalue' => '',
 330              'options' => '',
 331              'subtype' => 'r',
 332              'horizontal' => 0,
 333              'hidenoselect' => 1,
 334              'ignoreempty' => 0,
 335              'values' => "0/a\n1/b\n2/c\n3/d\n4/e"
 336          );
 337  
 338          $itemobj = new feedback_item_multichoicerated();
 339          $presentation = $itemobj->prepare_presentation_values_save(trim($record['values']),
 340              FEEDBACK_MULTICHOICERATED_VALUE_SEP2, FEEDBACK_MULTICHOICERATED_VALUE_SEP);
 341  
 342          if ($record['horizontal'] == 1 AND $record['subtype'] != 'd') {
 343              $presentation .= FEEDBACK_MULTICHOICERATED_ADJUST_SEP.'1';
 344          }
 345          $record['presentation'] = $record['subtype'].FEEDBACK_MULTICHOICERATED_TYPE_SEP.$presentation;
 346  
 347          $itemobj->set_data((object) $record);
 348          return $itemobj->save_item();
 349      }
 350  
 351      /**
 352       * Create numeric question item.
 353       *
 354       * @param object $feedback feedback record
 355       * @param array $record (optional) to override default values
 356       * @return int
 357       */
 358      public function create_item_numeric($feedback, $record = array()) {
 359          global $DB, $CFG;
 360  
 361          require_once($CFG->dirroot.'/mod/feedback/lib.php');
 362  
 363          $itemobj = feedback_get_item_class('numeric');
 364          $position = $DB->count_records('feedback_item', array('feedback' => $feedback->id)) + 1;
 365  
 366          $record = (array)$record + array(
 367              'id' => 0,
 368              'feedback' => $feedback->id,
 369              'template' => 0,
 370              'name' => 'Feedback question item ' . $position,
 371              'label' => 'Feedback label ' . $position,
 372              'presentation' => '',
 373              'typ' => 'numeric',
 374              'hasvalue' => 0,
 375              'position' => $position,
 376              'required' => 0,
 377              'dependitem' => 0,
 378              'dependvalue' => '',
 379              'options' => '',
 380              'rangefrom' => '-',
 381              'rangeto' => '-',
 382          );
 383  
 384          if ($record['rangefrom'] === '-' OR $record['rangeto'] === '-') {
 385              $record['presentation'] = $record['rangefrom'] . '|'. $record['rangeto'];
 386          } else if ($record['rangefrom'] > $record['rangeto']) {
 387              $record['presentation'] = $record['rangeto'] . '|'. $record['rangefrom'];
 388          } else {
 389              $record['presentation'] = $record['rangefrom'] . '|'. $record['rangeto'];
 390          }
 391  
 392          $itemobj->set_data((object) $record);
 393          return $itemobj->save_item();
 394      }
 395  
 396      /**
 397       * Create textarea question item.
 398       *
 399       * @param object $feedback feedback record
 400       * @param array $record (optional) to override default values
 401       * @return int
 402       */
 403      public function create_item_textarea($feedback, $record = array()) {
 404          global $DB, $CFG;
 405  
 406          require_once($CFG->dirroot.'/mod/feedback/lib.php');
 407  
 408          $itemobj = feedback_get_item_class('textarea');
 409          $position = $DB->count_records('feedback_item', array('feedback' => $feedback->id)) + 1;
 410  
 411          $record = (array)$record + array(
 412              'id' => 0,
 413              'feedback' => $feedback->id,
 414              'template' => 0,
 415              'name' => 'Feedback question item ' . $position,
 416              'label' => 'Feedback label ' . $position,
 417              'presentation' => '',
 418              'typ' => 'textarea',
 419              'hasvalue' => 0,
 420              'position' => $position,
 421              'required' => 0,
 422              'dependitem' => 0,
 423              'dependvalue' => '',
 424              'options' => '',
 425              'itemwidth' => '40',
 426              'itemheight' => '20',
 427          );
 428  
 429          $record['presentation'] = $record['itemwidth'] . '|'. $record['itemheight'];
 430  
 431          $itemobj->set_data((object) $record);
 432          return $itemobj->save_item();
 433      }
 434  
 435      /**
 436       * Create textfield question item.
 437       *
 438       * @param object $feedback feedback record
 439       * @param array $record (optional) to override default values
 440       * @return int
 441       */
 442      public function create_item_textfield($feedback, $record = array()) {
 443          global $DB, $CFG;
 444  
 445          require_once($CFG->dirroot.'/mod/feedback/lib.php');
 446  
 447          $itemobj = feedback_get_item_class('textfield');
 448          $position = $DB->count_records('feedback_item', array('feedback' => $feedback->id)) + 1;
 449  
 450          $record = (array)$record + array(
 451              'id' => 0,
 452              'feedback' => $feedback->id,
 453              'template' => 0,
 454              'name' => 'Feedback question item ' . $position,
 455              'label' => 'Feedback label ' . $position,
 456              'presentation' => '',
 457              'typ' => 'textfield',
 458              'hasvalue' => 0,
 459              'position' => $position,
 460              'required' => 0,
 461              'dependitem' => 0,
 462              'dependvalue' => '',
 463              'options' => '',
 464              'itemsize' => '20',
 465              'itemmaxlength' => '30',
 466          );
 467  
 468          $record['presentation'] = $record['itemsize'] . '|'. $record['itemmaxlength'];
 469  
 470          $itemobj->set_data((object) $record);
 471          return $itemobj->save_item();
 472      }
 473  
 474      /**
 475       * Create pagebreak.
 476       *
 477       * @param object $feedback feedback record
 478       * @return mixed false if there already is a pagebreak on last position or the id of the pagebreak-item
 479       */
 480      public function create_item_pagebreak($feedback) {
 481          global $CFG;
 482          require_once($CFG->dirroot.'/mod/feedback/lib.php');
 483  
 484          return feedback_create_pagebreak($feedback->id);
 485      }
 486  
 487      /**
 488       * Format feedback item values.
 489       *
 490       * This method will replace newline characters with the proper line separator for each question type.
 491       *
 492       * @param string $questiontype Question types
 493       * @param string $values Values
 494       * @return string Formatted values
 495       */
 496      protected function format_item_values(string $questiontype, string $values): string {
 497          global $CFG;
 498  
 499          if (!file_exists($CFG->dirroot.'/mod/feedback/item/'.$questiontype.'/lib.php')) {
 500              throw new coding_exception("Question type '$questiontype' not found");
 501          }
 502  
 503          require_once($CFG->dirroot.'/mod/feedback/item/'.$questiontype.'/lib.php');
 504  
 505          $questiontype = strtoupper($questiontype);
 506  
 507          if (defined("FEEDBACK_{$questiontype}_LINE_SEP")) {
 508              return implode(constant("FEEDBACK_{$questiontype}_LINE_SEP"), explode('\n', $values));
 509          }
 510  
 511          return $values;
 512      }
 513  
 514      /**
 515       * Given a response to a feedback item, return its corresponding value.
 516       *
 517       * @param mixed $record Item record
 518       * @param string $response Response name
 519       * @return int|string Response value
 520       */
 521      protected function get_item_response_value($record, string $response) {
 522          if (strpos($record->typ, 'multichoice') === 0) {
 523              $item = feedback_get_item_class($record->typ);
 524  
 525              return $this->get_choice_item_response_value($item, $record, $response);
 526          }
 527  
 528          return $response;
 529      }
 530  
 531      /**
 532       * Given a response to a feedback choice item, return its corresponding value.
 533       *
 534       * @param feedback_item_base $item Feedback item
 535       * @param mixed $record Item record
 536       * @param string $response Response
 537       * @param int $offset Choice to start looking from
 538       * @return int Response choice index
 539       */
 540      protected function get_choice_item_response_value(feedback_item_base $item, $record, string $response, int $offset = 1): int {
 541          $printval = $item->get_printval($record, (object) ['value' => $offset]);
 542  
 543          if (empty($printval)) {
 544              throw new coding_exception("Value '$offset' not found");
 545          }
 546  
 547          if ($printval === $response) {
 548              return $offset;
 549          }
 550  
 551          return $this->get_choice_item_response_value($item, $record, $response, $offset + 1);
 552      }
 553  }