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 311 and 401] [Versions 39 and 401] [Versions 400 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   * A class for loading and preparing grade data from import.
  19   *
  20   * @package   gradeimport_csv
  21   * @copyright 2014 Adrian Greeve <adrian@moodle.com>
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  /**
  28   * A class for loading and preparing grade data from import.
  29   *
  30   * @package   gradeimport_csv
  31   * @copyright 2014 Adrian Greeve <adrian@moodle.com>
  32   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   */
  34  class gradeimport_csv_load_data {
  35  
  36      /** @var string $error csv import error. */
  37      protected $error;
  38      /** @var int $iid Unique identifier for these csv records. */
  39      protected $iid;
  40      /** @var array $headers Column names for the data. */
  41      protected $headers;
  42      /** @var array $previewdata A subsection of the csv imported data. */
  43      protected $previewdata;
  44  
  45      // The map_user_data_with_value variables.
  46      /** @var array $newgrades Grades to be inserted into the gradebook. */
  47      protected $newgrades;
  48      /** @var array $newfeedbacks Feedback to be inserted into the gradebook. */
  49      protected $newfeedbacks;
  50      /** @var int $studentid Student ID*/
  51      protected $studentid;
  52  
  53      // The prepare_import_grade_data() variables.
  54      /** @var bool $status The current status of the import. True = okay, False = errors. */
  55      protected $status;
  56      /** @var int $importcode The code for this batch insert. */
  57      protected $importcode;
  58      /** @var array $gradebookerrors An array of errors from trying to import into the gradebook. */
  59      protected $gradebookerrors;
  60      /** @var array $newgradeitems An array of new grade items to be inserted into the gradebook. */
  61      protected $newgradeitems;
  62  
  63      /**
  64       * Load CSV content for previewing.
  65       *
  66       * @param string $text The grade data being imported.
  67       * @param string $encoding The type of encoding the file uses.
  68       * @param string $separator The separator being used to define each field.
  69       * @param int $previewrows How many rows are being previewed.
  70       */
  71      public function load_csv_content($text, $encoding, $separator, $previewrows) {
  72          $this->raise_limits();
  73  
  74          $this->iid = csv_import_reader::get_new_iid('grade');
  75          $csvimport = new csv_import_reader($this->iid, 'grade');
  76  
  77          $csvimport->load_csv_content($text, $encoding, $separator);
  78          $this->error = $csvimport->get_error();
  79  
  80          // If there are no import errors then proceed.
  81          if (empty($this->error)) {
  82  
  83              // Get header (field names).
  84              $this->headers = $csvimport->get_columns();
  85              $this->trim_headers();
  86  
  87              $csvimport->init();
  88              $this->previewdata = array();
  89  
  90              for ($numlines = 0; $numlines <= $previewrows; $numlines++) {
  91                  $lines = $csvimport->next();
  92                  if ($lines) {
  93                      $this->previewdata[] = $lines;
  94                  }
  95              }
  96          }
  97      }
  98  
  99      /**
 100       * Gets all of the grade items in this course.
 101       *
 102       * @param int $courseid Course id;
 103       * @return array An array of grade items for the course.
 104       */
 105      public static function fetch_grade_items($courseid) {
 106          $gradeitems = null;
 107          if ($allgradeitems = grade_item::fetch_all(array('courseid' => $courseid))) {
 108              foreach ($allgradeitems as $gradeitem) {
 109                  // Skip course type and category type.
 110                  if ($gradeitem->itemtype == 'course' || $gradeitem->itemtype == 'category') {
 111                      continue;
 112                  }
 113  
 114                  $displaystring = null;
 115                  if (!empty($gradeitem->itemmodule)) {
 116                      $displaystring = get_string('modulename', $gradeitem->itemmodule).get_string('labelsep', 'langconfig')
 117                              .$gradeitem->get_name();
 118                  } else {
 119                      $displaystring = $gradeitem->get_name();
 120                  }
 121                  $gradeitems[$gradeitem->id] = $displaystring;
 122              }
 123          }
 124          return $gradeitems;
 125      }
 126  
 127      /**
 128       * Cleans the column headers from the CSV file.
 129       */
 130      protected function trim_headers() {
 131          foreach ($this->headers as $i => $h) {
 132              $h = trim($h); // Remove whitespace.
 133              $h = clean_param($h, PARAM_RAW); // Clean the header.
 134              $this->headers[$i] = $h;
 135          }
 136      }
 137  
 138      /**
 139       * Raises the php execution time and memory limits for importing the CSV file.
 140       */
 141      protected function raise_limits() {
 142          // Large files are likely to take their time and memory. Let PHP know
 143          // that we'll take longer, and that the process should be recycled soon
 144          // to free up memory.
 145          core_php_time_limit::raise();
 146          raise_memory_limit(MEMORY_EXTRA);
 147      }
 148  
 149      /**
 150       * Inserts a record into the grade_import_values table. This also adds common record information.
 151       *
 152       * @param stdClass $record The grade record being inserted into the database.
 153       * @param int $studentid The student ID.
 154       * @param grade_item $gradeitem Grade item.
 155       * @return bool|int true or insert id on success. Null if the grade value is too high or too low or grade item not exist.
 156       */
 157      protected function insert_grade_record(stdClass $record, int $studentid, grade_item $gradeitem) {
 158          global $DB, $USER, $CFG;
 159          $record->importcode = $this->importcode;
 160          $record->userid     = $studentid;
 161          $record->importer   = $USER->id;
 162          // If the record final grade is set then check that the grade value isn't too high.
 163          // Final grade will not be set if we are inserting feedback.
 164          $gradepointmaximum = $gradeitem->grademax;
 165          $gradepointminimum = $gradeitem->grademin;
 166  
 167          $finalgradeinrange =
 168              isset($record->finalgrade) && $record->finalgrade <= $gradepointmaximum && $record->finalgrade >= $gradepointminimum;
 169          if (!isset($record->finalgrade) || $finalgradeinrange || $CFG->unlimitedgrades) {
 170              return $DB->insert_record('grade_import_values', $record);
 171          } else {
 172              if ($record->finalgrade > $gradepointmaximum) {
 173                  $this->cleanup_import(get_string('gradevaluetoobig', 'grades', format_float($gradepointmaximum)));
 174              } else {
 175                  $this->cleanup_import(get_string('gradevaluetoosmall', 'grades', format_float($gradepointminimum)));
 176              }
 177              return null;
 178          }
 179      }
 180  
 181      /**
 182       * Insert the new grade into the grade item buffer table.
 183       *
 184       * @param array $header The column headers from the CSV file.
 185       * @param int $key Current row identifier.
 186       * @param string $value The value for this row (final grade).
 187       * @return stdClass new grade that is ready for commiting to the gradebook.
 188       */
 189      protected function import_new_grade_item($header, $key, $value) {
 190          global $DB, $USER;
 191  
 192          // First check if header is already in temp database.
 193          if (empty($this->newgradeitems[$key])) {
 194  
 195              $newgradeitem = new stdClass();
 196              $newgradeitem->itemname = $header[$key];
 197              $newgradeitem->importcode = $this->importcode;
 198              $newgradeitem->importer = $USER->id;
 199  
 200              // Insert into new grade item buffer.
 201              $this->newgradeitems[$key] = $DB->insert_record('grade_import_newitem', $newgradeitem);
 202          }
 203          $newgrade = new stdClass();
 204          $newgrade->newgradeitem = $this->newgradeitems[$key];
 205  
 206          $trimmed = trim($value);
 207          if ($trimmed === '' or $trimmed == '-') {
 208              // Blank or dash grade means null, ie "no grade".
 209              $newgrade->finalgrade = null;
 210          } else {
 211              // We have an actual grade.
 212              $newgrade->finalgrade = $value;
 213          }
 214          $this->newgrades[] = $newgrade;
 215          return $newgrade;
 216      }
 217  
 218      /**
 219       * Check that the user is in the system.
 220       *
 221       * @param string $value The value, from the csv file, being mapped to identify the user.
 222       * @param array $userfields Contains the field and label being mapped from.
 223       * @return int Returns the user ID if it exists, otherwise null.
 224       */
 225      protected function check_user_exists($value, $userfields) {
 226          global $DB;
 227  
 228          $user = null;
 229          $errorkey = false;
 230          // The user may use the incorrect field to match the user. This could result in an exception.
 231          try {
 232              $field = $userfields['field'];
 233              // Fields that can be queried in a case-insensitive manner.
 234              $caseinsensitivefields = [
 235                  'email',
 236                  'username',
 237              ];
 238              // Build query predicate.
 239              if (in_array($field, $caseinsensitivefields)) {
 240                  // Case-insensitive.
 241                  $select = $DB->sql_equal($field, ':' . $field, false);
 242              } else {
 243                  // Exact-value.
 244                  $select = "{$field} = :{$field}";
 245              }
 246  
 247              // Validate if the user id value is numerical.
 248              if ($field === 'id' && !is_numeric($value)) {
 249                  $errorkey = 'usermappingerror';
 250              }
 251              // Make sure the record exists and that there's only one matching record found.
 252              $user = $DB->get_record_select('user', $select, array($userfields['field'] => $value), '*', MUST_EXIST);
 253          } catch (dml_missing_record_exception $missingex) {
 254              $errorkey = 'usermappingerror';
 255          } catch (dml_multiple_records_exception $multiex) {
 256              $errorkey = 'usermappingerrormultipleusersfound';
 257          }
 258          // Field may be fine, but no records were returned.
 259          if ($errorkey) {
 260              $usermappingerrorobj = new stdClass();
 261              $usermappingerrorobj->field = $userfields['label'];
 262              $usermappingerrorobj->value = $value;
 263              $this->cleanup_import(get_string($errorkey, 'grades', $usermappingerrorobj));
 264              unset($usermappingerrorobj);
 265              return null;
 266          }
 267          return $user->id;
 268      }
 269  
 270      /**
 271       * Check to see if the feedback matches a grade item.
 272       *
 273       * @param int $courseid The course ID.
 274       * @param int $itemid The ID of the grade item that the feedback relates to.
 275       * @param string $value The actual feedback being imported.
 276       * @return object Creates a feedback object with the item ID and the feedback value.
 277       */
 278      protected function create_feedback($courseid, $itemid, $value) {
 279          // Case of an id, only maps id of a grade_item.
 280          // This was idnumber.
 281          if (!new grade_item(array('id' => $itemid, 'courseid' => $courseid))) {
 282              // Supplied bad mapping, should not be possible since user
 283              // had to pick mapping.
 284              $this->cleanup_import(get_string('importfailed', 'grades'));
 285              return null;
 286          }
 287  
 288          // The itemid is the id of the grade item.
 289          $feedback = new stdClass();
 290          $feedback->itemid   = $itemid;
 291          $feedback->feedback = $value;
 292          return $feedback;
 293      }
 294  
 295      /**
 296       * This updates existing grade items.
 297       *
 298       * @param int $courseid The course ID.
 299       * @param array $map Mapping information provided by the user.
 300       * @param int $key The line that we are currently working on.
 301       * @param bool $verbosescales Form setting for grading with scales.
 302       * @param string $value The grade value.
 303       * @return array grades to be updated.
 304       */
 305      protected function update_grade_item($courseid, $map, $key, $verbosescales, $value) {
 306          // Case of an id, only maps id of a grade_item.
 307          // This was idnumber.
 308          if (!$gradeitem = new grade_item(array('id' => $map[$key], 'courseid' => $courseid))) {
 309              // Supplied bad mapping, should not be possible since user
 310              // had to pick mapping.
 311              $this->cleanup_import(get_string('importfailed', 'grades'));
 312              return null;
 313          }
 314  
 315          // Check if grade item is locked if so, abort.
 316          if ($gradeitem->is_locked()) {
 317              $this->cleanup_import(get_string('gradeitemlocked', 'grades'));
 318              return null;
 319          }
 320  
 321          $newgrade = new stdClass();
 322          $newgrade->itemid = $gradeitem->id;
 323          if ($gradeitem->gradetype == GRADE_TYPE_SCALE and $verbosescales) {
 324              if ($value === '' or $value == '-') {
 325                  $value = null; // No grade.
 326              } else {
 327                  $scale = $gradeitem->load_scale();
 328                  $scales = explode(',', $scale->scale);
 329                  $scales = array_map('trim', $scales); // Hack - trim whitespace around scale options.
 330                  array_unshift($scales, '-'); // Scales start at key 1.
 331                  $key = array_search($value, $scales);
 332                  if ($key === false) {
 333                      $this->cleanup_import(get_string('badgrade', 'grades'));
 334                      return null;
 335                  }
 336                  $value = $key;
 337              }
 338              $newgrade->finalgrade = $value;
 339          } else {
 340              if ($value === '' or $value == '-') {
 341                  $value = null; // No grade.
 342              } else {
 343                  // If the value has a local decimal or can correctly be unformatted, do it.
 344                  $validvalue = unformat_float($value, true);
 345                  if ($validvalue !== false) {
 346                      $value = $validvalue;
 347                  } else {
 348                      // Non numeric grade value supplied, possibly mapped wrong column.
 349                      $this->cleanup_import(get_string('badgrade', 'grades'));
 350                      return null;
 351                  }
 352              }
 353              $newgrade->finalgrade = $value;
 354          }
 355          $this->newgrades[] = $newgrade;
 356          return $this->newgrades;
 357      }
 358  
 359      /**
 360       * Clean up failed CSV grade import. Clears the temp table for inserting grades.
 361       *
 362       * @param string $notification The error message to display from the unsuccessful grade import.
 363       */
 364      protected function cleanup_import($notification) {
 365          $this->status = false;
 366          import_cleanup($this->importcode);
 367          $this->gradebookerrors[] = $notification;
 368      }
 369  
 370      /**
 371       * Check user mapping.
 372       *
 373       * @param string $mappingidentifier The user field that we are matching together.
 374       * @param string $value The value we are checking / importing.
 375       * @param array $header The column headers of the csv file.
 376       * @param array $map Mapping information provided by the user.
 377       * @param int $key Current row identifier.
 378       * @param int $courseid The course ID.
 379       * @param int $feedbackgradeid The ID of the grade item that the feedback relates to.
 380       * @param bool $verbosescales Form setting for grading with scales.
 381       */
 382      protected function map_user_data_with_value($mappingidentifier, $value, $header, $map, $key, $courseid, $feedbackgradeid,
 383              $verbosescales) {
 384  
 385          // Fields that the user can be mapped from.
 386          $userfields = array(
 387              'userid' => array(
 388                  'field' => 'id',
 389                  'label' => 'id',
 390              ),
 391              'useridnumber' => array(
 392                  'field' => 'idnumber',
 393                  'label' => 'idnumber',
 394              ),
 395              'useremail' => array(
 396                  'field' => 'email',
 397                  'label' => 'email address',
 398              ),
 399              'username' => array(
 400                  'field' => 'username',
 401                  'label' => 'username',
 402              ),
 403          );
 404  
 405          switch ($mappingidentifier) {
 406              case 'userid':
 407              case 'useridnumber':
 408              case 'useremail':
 409              case 'username':
 410                  $this->studentid = $this->check_user_exists($value, $userfields[$mappingidentifier]);
 411              break;
 412              case 'new':
 413                  $this->import_new_grade_item($header, $key, $value);
 414              break;
 415              case 'feedback':
 416                  if ($feedbackgradeid) {
 417                      $feedback = $this->create_feedback($courseid, $feedbackgradeid, $value);
 418                      if (isset($feedback)) {
 419                          $this->newfeedbacks[] = $feedback;
 420                      }
 421                  }
 422              break;
 423              default:
 424                  // Existing grade items.
 425                  if (!empty($map[$key])) {
 426                      $this->newgrades = $this->update_grade_item($courseid, $map, $key, $verbosescales, $value,
 427                              $mappingidentifier);
 428                  }
 429                  // Otherwise, we ignore this column altogether because user has chosen
 430                  // to ignore them (e.g. institution, address etc).
 431              break;
 432          }
 433      }
 434  
 435      /**
 436       * Checks and prepares grade data for inserting into the gradebook.
 437       *
 438       * @param array $header Column headers of the CSV file.
 439       * @param object $formdata Mapping information from the preview page.
 440       * @param object $csvimport csv import reader object for iterating over the imported CSV file.
 441       * @param int $courseid The course ID.
 442       * @param bool $separatemode If we have groups are they separate?
 443       * @param mixed $currentgroup current group information.
 444       * @param bool $verbosescales Form setting for grading with scales.
 445       * @return bool True if the status for importing is okay, false if there are errors.
 446       */
 447      public function prepare_import_grade_data($header, $formdata, $csvimport, $courseid, $separatemode, $currentgroup,
 448              $verbosescales) {
 449          global $DB, $USER;
 450  
 451          // The import code is used for inserting data into the grade tables.
 452          $this->importcode = $formdata->importcode;
 453          $this->status = true;
 454          $this->headers = $header;
 455          $this->studentid = null;
 456          $this->gradebookerrors = null;
 457          $forceimport = $formdata->forceimport;
 458          // Temporary array to keep track of what new headers are processed.
 459          $this->newgradeitems = array();
 460          $this->trim_headers();
 461          $timeexportkey = null;
 462          $map = array();
 463          // Loops mapping_0, mapping_1 .. mapping_n and construct $map array.
 464          foreach ($header as $i => $head) {
 465              if (isset($formdata->{'mapping_'.$i})) {
 466                  $map[$i] = $formdata->{'mapping_'.$i};
 467              }
 468              if ($head == get_string('timeexported', 'gradeexport_txt')) {
 469                  $timeexportkey = $i;
 470              }
 471          }
 472  
 473          // If mapping information is supplied.
 474          $map[clean_param($formdata->mapfrom, PARAM_RAW)] = clean_param($formdata->mapto, PARAM_RAW);
 475  
 476          // Check for mapto collisions.
 477          $maperrors = array();
 478          foreach ($map as $i => $j) {
 479              if ($j == 0) {
 480                  // You can have multiple ignores.
 481                  continue;
 482              } else {
 483                  if (!isset($maperrors[$j])) {
 484                      $maperrors[$j] = true;
 485                  } else {
 486                      // Collision.
 487                      throw new \moodle_exception('cannotmapfield', '', '', $j);
 488                  }
 489              }
 490          }
 491  
 492          $this->raise_limits();
 493  
 494          $csvimport->init();
 495  
 496          while ($line = $csvimport->next()) {
 497              if (count($line) <= 1) {
 498                  // There is no data on this line, move on.
 499                  continue;
 500              }
 501  
 502              // Array to hold all grades to be inserted.
 503              $this->newgrades = array();
 504              // Array to hold all feedback.
 505              $this->newfeedbacks = array();
 506              // Each line is a student record.
 507              foreach ($line as $key => $value) {
 508  
 509                  $value = clean_param($value, PARAM_RAW);
 510                  $value = trim($value);
 511  
 512                  /*
 513                   * the options are
 514                   * 1) userid, useridnumber, usermail, username - used to identify user row
 515                   * 2) new - new grade item
 516                   * 3) id - id of the old grade item to map onto
 517                   * 3) feedback_id - feedback for grade item id
 518                   */
 519  
 520                  // Explode the mapping for feedback into a label 'feedback' and the identifying number.
 521                  $mappingbase = explode("_", $map[$key]);
 522                  $mappingidentifier = $mappingbase[0];
 523                  // Set the feedback identifier if it exists.
 524                  if (isset($mappingbase[1])) {
 525                      $feedbackgradeid = (int)$mappingbase[1];
 526                  } else {
 527                      $feedbackgradeid = '';
 528                  }
 529  
 530                  $this->map_user_data_with_value($mappingidentifier, $value, $header, $map, $key, $courseid, $feedbackgradeid,
 531                          $verbosescales);
 532                  if ($this->status === false) {
 533                      return $this->status;
 534                  }
 535              }
 536  
 537              // No user mapping supplied at all, or user mapping failed.
 538              if (empty($this->studentid) || !is_numeric($this->studentid)) {
 539                  // User not found, abort whole import.
 540                  $this->cleanup_import(get_string('usermappingerrorusernotfound', 'grades'));
 541                  break;
 542              }
 543  
 544              if ($separatemode and !groups_is_member($currentgroup, $this->studentid)) {
 545                  // Not allowed to import into this group, abort.
 546                  $this->cleanup_import(get_string('usermappingerrorcurrentgroup', 'grades'));
 547                  break;
 548              }
 549  
 550              // Insert results of this students into buffer.
 551              if ($this->status and !empty($this->newgrades)) {
 552  
 553                  foreach ($this->newgrades as $newgrade) {
 554  
 555                      // Check if grade_grade is locked and if so, abort.
 556                      if (!empty($newgrade->itemid) and $gradegrade = new grade_grade(array('itemid' => $newgrade->itemid,
 557                              'userid' => $this->studentid))) {
 558                          if ($gradegrade->is_locked()) {
 559                              // Individual grade locked.
 560                              $this->cleanup_import(get_string('gradelocked', 'grades'));
 561                              return $this->status;
 562                          }
 563                          // Check if the force import option is disabled and the last exported date column is present.
 564                          if (!$forceimport && !empty($timeexportkey)) {
 565                              $exportedtime = $line[$timeexportkey];
 566                              if (clean_param($exportedtime, PARAM_INT) != $exportedtime || $exportedtime > time() ||
 567                                      $exportedtime < strtotime("-1 year", time())) {
 568                                  // The date is invalid, or in the future, or more than a year old.
 569                                  $this->cleanup_import(get_string('invalidgradeexporteddate', 'grades'));
 570                                  return $this->status;
 571  
 572                              }
 573                              $timemodified = $gradegrade->get_dategraded();
 574                              if (!empty($timemodified) && ($exportedtime < $timemodified)) {
 575                                  // The item was graded after we exported it, we return here not to override it.
 576                                  $user = core_user::get_user($this->studentid);
 577                                  $this->cleanup_import(get_string('gradealreadyupdated', 'grades', fullname($user)));
 578                                  return $this->status;
 579                              }
 580                          }
 581                      }
 582                      if (isset($newgrade->itemid)) {
 583                          $gradeitem = new grade_item(['id' => $newgrade->itemid]);
 584                      } else if (isset($newgrade->newgradeitem)) {
 585                          $gradeitem = new grade_item(['id' => $newgrade->newgradeitem]);
 586                      }
 587                      $insertid = isset($gradeitem) ? self::insert_grade_record($newgrade, $this->studentid, $gradeitem) : null;
 588                      // Check to see if the insert was successful.
 589                      if (empty($insertid)) {
 590                          return null;
 591                      }
 592                  }
 593              }
 594  
 595              // Updating/inserting all comments here.
 596              if ($this->status and !empty($this->newfeedbacks)) {
 597                  foreach ($this->newfeedbacks as $newfeedback) {
 598                      $sql = "SELECT *
 599                                FROM {grade_import_values}
 600                               WHERE importcode=? AND userid=? AND itemid=? AND importer=?";
 601                      if ($feedback = $DB->get_record_sql($sql, array($this->importcode, $this->studentid, $newfeedback->itemid,
 602                              $USER->id))) {
 603                          $newfeedback->id = $feedback->id;
 604                          $DB->update_record('grade_import_values', $newfeedback);
 605  
 606                      } else {
 607                          // The grade item for this is not updated.
 608                          $newfeedback->importonlyfeedback = true;
 609                          $insertid = self::insert_grade_record($newfeedback, $this->studentid, new grade_item(['id' => $newfeedback->itemid]));
 610                          // Check to see if the insert was successful.
 611                          if (empty($insertid)) {
 612                              return null;
 613                          }
 614                      }
 615                  }
 616              }
 617          }
 618          return $this->status;
 619      }
 620  
 621      /**
 622       * Returns the headers parameter for this class.
 623       *
 624       * @return array returns headers parameter for this class.
 625       */
 626      public function get_headers() {
 627          return $this->headers;
 628      }
 629  
 630      /**
 631       * Returns the error parameter for this class.
 632       *
 633       * @return string returns error parameter for this class.
 634       */
 635      public function get_error() {
 636          return $this->error;
 637      }
 638  
 639      /**
 640       * Returns the iid parameter for this class.
 641       *
 642       * @return int returns iid parameter for this class.
 643       */
 644      public function get_iid() {
 645          return $this->iid;
 646      }
 647  
 648      /**
 649       * Returns the preview_data parameter for this class.
 650       *
 651       * @return array returns previewdata parameter for this class.
 652       */
 653      public function get_previewdata() {
 654          return $this->previewdata;
 655      }
 656  
 657      /**
 658       * Returns the gradebookerrors parameter for this class.
 659       *
 660       * @return array returns gradebookerrors parameter for this class.
 661       */
 662      public function get_gradebookerrors() {
 663          return $this->gradebookerrors;
 664      }
 665  }