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.
   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   * @package   moodlecore
  18   * @subpackage backup-imscc
  19   * @copyright 2009 Mauro Rondinelli (mauro.rondinelli [AT] uvcms.com)
  20   * @copyright 2011 Darko Miletic <dmiletic@moodlerooms.com>
  21   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22   */
  23  
  24  defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');
  25  
  26  class cc11_quiz extends entities11 {
  27  
  28      public function generate_node_question_categories () {
  29  
  30          $instances = $this->generate_instances();
  31  
  32          $node_course_question_categories = $this->create_node_course_question_categories($instances);
  33          $node_course_question_categories = empty($node_course_question_categories) ? '' : $node_course_question_categories;
  34  
  35          return $node_course_question_categories;
  36  
  37      }
  38  
  39      public function generate_node_course_modules_mod () {
  40  
  41          cc112moodle::log_action('Creating Quiz mods');
  42  
  43          $node_course_modules_mod = '';
  44          $instances = $this->generate_instances();
  45  
  46          if (!empty($instances)) {
  47              foreach ($instances as $instance) {
  48                  if ($instance['is_question_bank'] == 0) {
  49                      $node_course_modules_mod .= $this->create_node_course_modules_mod($instance);
  50                  }
  51              }
  52          }
  53  
  54          return $node_course_modules_mod;
  55  
  56      }
  57  
  58      private function create_node_course_modules_mod_quiz_feedback () {
  59  
  60          $sheet_question_mod_feedback = cc112moodle::loadsheet(SHEET_COURSE_SECTIONS_SECTION_MODS_MOD_QUIZ_FEEDBACK);
  61  
  62          return $sheet_question_mod_feedback;
  63      }
  64  
  65      private function generate_instances () {
  66  
  67          $last_instance_id = 0;
  68          $last_question_id = 0;
  69          $last_answer_id = 0;
  70  
  71          $instances = array();
  72  
  73          $types = array(MOODLE_TYPE_QUIZ, MOODLE_TYPE_QUESTION_BANK);
  74  
  75          foreach ($types as $type) {
  76  
  77              if (!empty(cc112moodle::$instances['instances'][$type])) {
  78  
  79                  foreach (cc112moodle::$instances['instances'][$type] as $instance) {
  80  
  81                      if ($type == MOODLE_TYPE_QUIZ) {
  82                          $is_question_bank = 0;
  83                      } else {
  84                          $is_question_bank = 1;
  85                      }
  86  
  87                      $assessment_file = $this->get_external_xml($instance['resource_indentifier']);
  88  
  89                      if (!empty($assessment_file)) {
  90  
  91                          $assessment = $this->load_xml_resource(cc112moodle::$path_to_manifest_folder . DIRECTORY_SEPARATOR . $assessment_file);
  92  
  93                          if (!empty($assessment)) {
  94  
  95                              $replace_values = array('unlimited' => 0);
  96  
  97                              $questions = $this->get_questions($assessment, $last_question_id, $last_answer_id, dirname($assessment_file), $is_question_bank);
  98                              $question_count = count($questions);
  99  
 100                              if (!empty($question_count)) {
 101  
 102                                  $last_instance_id++;
 103  
 104                                  $instances[$instance['resource_indentifier']]['questions'] = $questions;
 105                                  $instances[$instance['resource_indentifier']]['id'] = $last_instance_id;
 106                                  $instances[$instance['resource_indentifier']]['title'] = $instance['title'];
 107                                  $instances[$instance['resource_indentifier']]['is_question_bank'] = $is_question_bank;
 108                                  $instances[$instance['resource_indentifier']]['options']['timelimit'] = $this->get_global_config($assessment, 'qmd_timelimit', 0);
 109                                  $instances[$instance['resource_indentifier']]['options']['max_attempts'] = $this->get_global_config($assessment, 'cc_maxattempts', 0, $replace_values);
 110                              }
 111                          }
 112                      }
 113                  }
 114              }
 115          }
 116  
 117          return $instances;
 118      }
 119  
 120  
 121      private function create_node_course_modules_mod ($instance) {
 122  
 123          $sheet_question_mod = cc112moodle::loadsheet(SHEET_COURSE_SECTIONS_SECTION_MODS_MOD_QUIZ);
 124  
 125          $node_course_modules_quiz_question_instances = $this->create_node_course_modules_mod_quiz_question_instances($instance);
 126          $node_course_modules_quiz_feedback = $this->create_node_course_modules_mod_quiz_feedback($instance);
 127  
 128          $questions_strings = $this->get_questions_string($instance);
 129          $quiz_stamp = 'localhost+' . time() . '+' . $this->generate_random_string(6);
 130  
 131          $find_tags = array('[#mod_id#]',
 132                             '[#mod_name#]',
 133                             '[#mod_intro#]',
 134                             '[#mod_stamp#]',
 135                             '[#question_string#]',
 136                             '[#date_now#]',
 137                             '[#mod_max_attempts#]',
 138                             '[#mod_timelimit#]',
 139                             '[#node_question_instance#]',
 140                             '[#node_questions_feedback#]');
 141  
 142          $replace_values = array($instance['id'],
 143                                  self::safexml($instance['title']),
 144                                  self::safexml($instance['title']),
 145                                  self::safexml($quiz_stamp),
 146                                  self::safexml($questions_strings),
 147                                  time(),
 148                                  $instance['options']['max_attempts'],
 149                                  $instance['options']['timelimit'],
 150                                  $node_course_modules_quiz_question_instances,
 151                                  $node_course_modules_quiz_feedback); //this one has tags
 152  
 153          $node_question_mod = str_replace($find_tags, $replace_values, $sheet_question_mod);
 154  
 155          return $node_question_mod;
 156      }
 157  
 158      private function get_global_config ($assessment, $option, $default_value, $replace_values = '') {
 159  
 160          $xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
 161          $metadata = $xpath->query('/xmlns:questestinterop/xmlns:assessment/xmlns:qtimetadata/xmlns:qtimetadatafield');
 162  
 163          foreach ($metadata as $field) {
 164              $field_label = $xpath->query('xmlns:fieldlabel', $field);
 165              $field_label = !empty($field_label->item(0)->nodeValue) ? $field_label->item(0)->nodeValue : '';
 166  
 167              if (strtolower($field_label) == strtolower($option)) {
 168                  $field_entry = $xpath->query('xmlns:fieldentry', $field);
 169                  $response = !empty($field_entry->item(0)->nodeValue) ? $field_entry->item(0)->nodeValue : '';
 170              }
 171          }
 172  
 173          $response = !empty($response) ? trim($response) : '';
 174  
 175          if (!empty($replace_values)) {
 176              foreach ($replace_values as $key => $value) {
 177                  $response = ($key == $response) ? $value : $response;
 178              }
 179          }
 180  
 181          $response = empty($response) ? $default_value : $response;
 182  
 183          return $response;
 184      }
 185  
 186      private function create_node_course_modules_mod_quiz_question_instances ($instance) {
 187  
 188          $node_course_module_mod_quiz_questions_instances = '';
 189          $sheet_question_mod_instance = cc112moodle::loadsheet(SHEET_COURSE_SECTIONS_SECTION_MODS_MOD_QUIZ_QUESTION_INSTANCE);
 190  
 191          $find_tags = array('[#question_id#]' , '[#instance_id#]');
 192  
 193          if (!empty($instance['questions'])) {
 194  
 195              foreach ($instance['questions'] as $question) {
 196                  $replace_values = array($question['id'] , $question['id']);
 197                  $node_course_module_mod_quiz_questions_instances .= str_replace($find_tags, $replace_values, $sheet_question_mod_instance);
 198              }
 199  
 200              $node_course_module_mod_quiz_questions_instances = str_replace($find_tags, $replace_values, $node_course_module_mod_quiz_questions_instances);
 201          }
 202  
 203          return $node_course_module_mod_quiz_questions_instances;
 204      }
 205  
 206      private function get_questions_string ($instance) {
 207  
 208          $questions_string = '';
 209  
 210          if (!empty($instance['questions'])) {
 211              foreach ($instance['questions'] as $question) {
 212                  $questions_string .= $question['id'] . ',';
 213              }
 214          }
 215  
 216          $questions_string = !empty($questions_string) ? substr($questions_string, 0, strlen($questions_string) - 1) : '';
 217  
 218          return $questions_string;
 219      }
 220  
 221      private function create_node_course_question_categories ($instances) {
 222  
 223          $sheet_question_categories = cc112moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES);
 224  
 225          if (!empty($instances)) {
 226  
 227              $node_course_question_categories_question_category = '';
 228  
 229              foreach ($instances as $instance) {
 230                  $node_course_question_categories_question_category .= $this->create_node_course_question_categories_question_category($instance);
 231              }
 232  
 233              $find_tags = array('[#node_course_question_categories_question_category#]');
 234              $replace_values = array($node_course_question_categories_question_category);
 235  
 236              $node_course_question_categories = str_replace($find_tags, $replace_values, $sheet_question_categories);
 237          }
 238  
 239          $node_course_question_categories = empty($node_course_question_categories) ? '' : $node_course_question_categories;
 240  
 241          return $node_course_question_categories;
 242      }
 243  
 244      private function create_node_course_question_categories_question_category ($instance) {
 245  
 246          $sheet_question_categories_quetion_category = cc112moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES_QUESTION_CATEGORY);
 247  
 248          $find_tags = array('[#quiz_id#]',
 249                             '[#quiz_name#]',
 250                             '[#quiz_stamp#]',
 251                             '[#node_course_question_categories_question_category_questions#]');
 252  
 253          $node_course_question_categories_questions = $this->create_node_course_question_categories_question_category_question($instance);
 254          $node_course_question_categories_questions = empty($node_course_question_categories_questions) ? '' : $node_course_question_categories_questions;
 255  
 256          $quiz_stamp = 'localhost+' . time() . '+' . $this->generate_random_string(6);
 257  
 258          $replace_values = array($instance['id'],
 259                                  self::safexml($instance['title']),
 260                                  $quiz_stamp,
 261                                  $node_course_question_categories_questions);
 262  
 263          $node_question_categories = str_replace($find_tags, $replace_values, $sheet_question_categories_quetion_category);
 264  
 265          return $node_question_categories;
 266      }
 267  
 268      private function create_node_course_question_categories_question_category_question ($instance) {
 269  
 270          global $USER;
 271  
 272          $node_course_question_categories_question = '';
 273  
 274          $find_tags = array('[#question_id#]',
 275                             '[#question_title#]',
 276                             '[#question_text#]',
 277                             '[#question_type#]',
 278                             '[#question_general_feedback#]',
 279                             '[#question_defaultgrade#]',
 280                             '[#date_now#]',
 281                             '[#question_type_nodes#]',
 282                             '[#question_stamp#]',
 283                             '[#question_version#]',
 284                             '[#logged_user#]');
 285  
 286          $sheet_question_categories_question = cc112moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES_QUESTION_CATEGORY_QUESTION);
 287  
 288          $questions = $instance['questions'];
 289  
 290          if (!empty($questions)) {
 291  
 292              foreach ($questions as $question) {
 293  
 294                  $quiz_stamp = 'localhost+' . time() . '+' . $this->generate_random_string(6);
 295                  $quiz_version = 'localhost+' . time() . '+' . $this->generate_random_string(6);
 296  
 297                  $question_moodle_type = $question['moodle_type'];
 298                  $question_cc_type = $question['cc_type'];
 299  
 300                  $question_type_node = '';
 301  
 302                  $question_type_node = ($question_moodle_type == MOODLE_QUIZ_MULTIPLE_CHOICE) ? $this->create_node_course_question_categories_question_category_question_multiple_choice($question) : $question_type_node;
 303                  $question_type_node = ($question_moodle_type == MOODLE_QUIZ_TRUE_FALSE) ? $this->create_node_course_question_categories_question_category_question_true_false($question) : $question_type_node;
 304                  $question_type_node = ($question_moodle_type == MOODLE_QUIZ_ESSAY) ? $this->create_node_course_question_categories_question_category_question_eesay($question) : $question_type_node;
 305                  $question_type_node = ($question_moodle_type == MOODLE_QUIZ_SHORTANSWER) ? $this->create_node_course_question_categories_question_category_question_shortanswer($question) : $question_type_node;
 306  
 307                  $questionname = !empty($question['name']) ? self::safexml($question['name']) : self::safexml($this->truncate_text($question['title'], 255, true));
 308                  $replace_values = array($question['id'],
 309                                          $questionname,
 310                                          self::safexml($question['title']),
 311                                          $question_moodle_type,
 312                                          self::safexml($question['feedback']),
 313                                          $question['defaultgrade'],
 314                                          time(),
 315                                          $question_type_node,
 316                                          $quiz_stamp,
 317                                          $quiz_version,
 318                                          $USER->id);
 319  
 320                  $node_course_question_categories_question .= str_replace($find_tags, $replace_values, $sheet_question_categories_question);
 321              }
 322          }
 323  
 324          $node_course_question_categories_question = empty($node_course_question_categories_question) ? '' : $node_course_question_categories_question;
 325  
 326          return $node_course_question_categories_question;
 327      }
 328  
 329      private function get_questions ($assessment, &$last_question_id, &$last_answer_id, $root_path, $is_question_bank) {
 330  
 331          $questions = array();
 332  
 333          $xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
 334  
 335          if (!$is_question_bank) {
 336              $questions_items = $xpath->query('/xmlns:questestinterop/xmlns:assessment/xmlns:section/xmlns:item');
 337          } else {
 338              $questions_items = $xpath->query('/xmlns:questestinterop/xmlns:objectbank/xmlns:item');
 339          }
 340  
 341          foreach ($questions_items as $question_item) {
 342  
 343              $count_questions = $xpath->evaluate('count(xmlns:presentation/xmlns:flow/xmlns:material/xmlns:mattext)', $question_item);
 344  
 345              if ($count_questions == 0) {
 346                  $question_title = $xpath->query('xmlns:presentation/xmlns:material/xmlns:mattext', $question_item);
 347              } else {
 348                  $question_title = $xpath->query('xmlns:presentation/xmlns:flow/xmlns:material/xmlns:mattext', $question_item);
 349              }
 350  
 351              $question_title = !empty($question_title->item(0)->nodeValue) ? $question_title->item(0)->nodeValue : '';
 352  
 353              $question_identifier = $xpath->query('@ident', $question_item);
 354              $question_identifier = !empty($question_identifier->item(0)->nodeValue) ? $question_identifier->item(0)->nodeValue : '';
 355  
 356              if (!empty($question_identifier)) {
 357  
 358                  $question_type = $this->get_question_type($question_identifier, $assessment);
 359  
 360                  if (!empty($question_type['moodle'])) {
 361  
 362                      $last_question_id++;
 363  
 364                      $questions[$question_identifier]['id'] = $last_question_id;
 365  
 366                      $question_title = $this->update_sources($question_title, $root_path);
 367                      $question_title = !empty($question_title) ? str_replace("%24", "\$", $this->include_titles($question_title)) : '';
 368  
 369                      // This attribute is not IMSCC spec, but it is included in Moodle 2.x export of IMS1.1
 370                      $questionname = $xpath->query('@title', $question_item);
 371                      $questionname = !empty($questionname->item(0)->nodeValue) ? $questionname->item(0)->nodeValue : '';
 372  
 373                      $questions[$question_identifier]['title'] = $question_title;
 374                      $questions[$question_identifier]['name'] = $questionname;
 375                      $questions[$question_identifier]['identifier'] = $question_identifier;
 376                      $questions[$question_identifier]['moodle_type'] = $question_type['moodle'];
 377                      $questions[$question_identifier]['cc_type'] = $question_type['cc'];
 378                      $questions[$question_identifier]['feedback'] = $this->get_general_feedback($assessment, $question_identifier);
 379                      $questions[$question_identifier]['defaultgrade'] = $this->get_defaultgrade($assessment, $question_identifier);
 380                      $questions[$question_identifier]['answers'] = $this->get_answers($question_identifier, $assessment, $last_answer_id);
 381  
 382                  }
 383              }
 384          }
 385  
 386          $questions = !empty($questions) ? $questions : '';
 387  
 388          return $questions;
 389      }
 390  
 391      private function str_replace_once ($search, $replace, $subject) {
 392  
 393          $first_char = strpos($subject, $search);
 394  
 395          if ($first_char !== false) {
 396  
 397              $before_str = substr($subject, 0, $first_char);
 398              $after_str = substr($subject, $first_char + strlen($search));
 399  
 400              return $before_str . $replace . $after_str;
 401  
 402          } else {
 403              return $subject;
 404          }
 405      }
 406  
 407      private function get_defaultgrade($assessment, $question_identifier) {
 408          $result = 1;
 409          $xpath = cc2moodle::newx_path($assessment, cc2moodle::getquizns());
 410          $query = '//xmlns:item[@ident="' . $question_identifier . '"]';
 411          $query .= '//xmlns:qtimetadatafield[xmlns:fieldlabel="cc_weighting"]/xmlns:fieldentry';
 412          $defgrade = $xpath->query($query);
 413          if (!empty($defgrade) && ($defgrade->length > 0)) {
 414              $resp = (int)$defgrade->item(0)->nodeValue;
 415              if ($resp >= 0 && $resp <= 99) {
 416                  $result = $resp;
 417              }
 418          }
 419          return $result;
 420      }
 421  
 422      private function get_general_feedback ($assessment, $question_identifier) {
 423  
 424          $xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
 425  
 426          $respconditions = $xpath->query('//xmlns:item[@ident="' . $question_identifier . '"]/xmlns:resprocessing/xmlns:respcondition');
 427  
 428          if (!empty($respconditions)) {
 429  
 430              foreach ($respconditions as $respcondition) {
 431  
 432                  $continue = $respcondition->getAttributeNode('continue');
 433                  $continue = !empty($continue->nodeValue) ? strtolower($continue->nodeValue) : '';
 434  
 435                  if ($continue == 'yes') {
 436  
 437                      $display_feedback = $xpath->query('xmlns:displayfeedback', $respcondition);
 438  
 439                      if (!empty($display_feedback)) {
 440                          foreach ($display_feedback as $feedback) {
 441  
 442                              $feedback_identifier = $feedback->getAttributeNode('linkrefid');
 443                              $feedback_identifier = !empty($feedback_identifier->nodeValue) ? $feedback_identifier->nodeValue : '';
 444  
 445                              if (!empty($feedback_identifier)) {
 446                                  $feedbacks_identifiers[] = $feedback_identifier;
 447                              }
 448                          }
 449                      }
 450                  }
 451              }
 452          }
 453  
 454          $feedback = '';
 455          $feedbacks_identifiers = empty($feedbacks_identifiers) ? '' : $feedbacks_identifiers;
 456  
 457          if (!empty($feedbacks_identifiers)) {
 458              foreach ($feedbacks_identifiers as $feedback_identifier) {
 459                  $feedbacks = $xpath->query('//xmlns:item[@ident="' . $question_identifier . '"]/xmlns:itemfeedback[@ident="' . $feedback_identifier . '"]/xmlns:flow_mat/xmlns:material/xmlns:mattext');
 460                  $feedback .= !empty($feedbacks->item(0)->nodeValue) ? $feedbacks->item(0)->nodeValue . ' ' : '';
 461              }
 462          }
 463  
 464          return $feedback;
 465      }
 466  
 467      private function get_feedback ($assessment, $identifier, $item_identifier, $question_type) {
 468  
 469          $xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
 470  
 471          $resource_processing = $xpath->query('//xmlns:item[@ident="' . $item_identifier . '"]/xmlns:resprocessing/xmlns:respcondition');
 472  
 473          if (!empty($resource_processing)) {
 474  
 475              foreach ($resource_processing as $response) {
 476  
 477                  $varequal = $xpath->query('xmlns:conditionvar/xmlns:varequal', $response);
 478                  $varequal = !empty($varequal->item(0)->nodeValue) ? $varequal->item(0)->nodeValue : '';
 479  
 480                  if (strtolower($varequal) == strtolower($identifier) || ($question_type == CC_QUIZ_ESSAY)) {
 481  
 482                      $display_feedback = $xpath->query('xmlns:displayfeedback', $response);
 483  
 484                      if (!empty($display_feedback)) {
 485                          foreach ($display_feedback as $feedback) {
 486  
 487                              $feedback_identifier = $feedback->getAttributeNode('linkrefid');
 488                              $feedback_identifier = !empty($feedback_identifier->nodeValue) ? $feedback_identifier->nodeValue : '';
 489  
 490                              if (!empty($feedback_identifier)) {
 491                                  $feedbacks_identifiers[] = $feedback_identifier;
 492                              }
 493                          }
 494                      }
 495                  }
 496              }
 497          }
 498  
 499          $feedback = '';
 500          $feedbacks_identifiers = empty($feedbacks_identifiers) ? '' : $feedbacks_identifiers;
 501  
 502          if (!empty($feedbacks_identifiers)) {
 503              foreach ($feedbacks_identifiers as $feedback_identifier) {
 504                  $feedbacks = $xpath->query('//xmlns:item[@ident="' . $item_identifier . '"]/xmlns:itemfeedback[@ident="' . $feedback_identifier . '"]/xmlns:flow_mat/xmlns:material/xmlns:mattext');
 505                  $feedback .= !empty($feedbacks->item(0)->nodeValue) ? $feedbacks->item(0)->nodeValue . ' ' : '';
 506              }
 507          }
 508  
 509          return $feedback;
 510      }
 511  
 512      private function get_answers_fib ($question_identifier, $identifier, $assessment, &$last_answer_id) {
 513  
 514          $xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
 515  
 516          $correctanswersfib = array();
 517          $incorrectanswersfib = array();
 518  
 519          $response_items = $xpath->query('//xmlns:item[@ident="' . $question_identifier . '"]/xmlns:resprocessing/xmlns:respcondition');
 520  
 521          $correctrespcond = $xpath->query('//xmlns:item[@ident="' . $question_identifier . '"]/xmlns:resprocessing/xmlns:respcondition/xmlns:setvar[text()="100"]/..');
 522          $correctanswers = $xpath->query('xmlns:conditionvar/xmlns:varequal', $correctrespcond->item(0));
 523  
 524          // Correct answers.
 525          foreach ($correctanswers as $correctans) {
 526              $answertitle = !empty($correctans->nodeValue) ? $correctans->nodeValue : '';
 527              if (empty($answertitle)) {
 528                  continue;
 529              }
 530  
 531              $last_answer_id++;
 532  
 533              $correctanswersfib[$answertitle] = array(
 534                  'id' => $last_answer_id,
 535                  'title' => $answertitle,
 536                  'score' => 1,
 537                  'feedback' => '',
 538                  'case' => 0);
 539          }
 540  
 541          // Handle incorrect answers and feedback for all items.
 542          foreach ($response_items as $response_item) {
 543  
 544              $setvar = $xpath->query('xmlns:setvar', $response_item);
 545              if (!empty($setvar->length) && $setvar->item(0)->nodeValue == '100') {
 546                  // Skip the correct answer responsecondition.
 547                  continue;
 548              }
 549  
 550              $varequal = $xpath->query('xmlns:conditionvar/xmlns:varequal', $response_item);
 551              if (empty($varequal->length)) {
 552                  // Skip respcondition elements that don't have varequal containing an answer
 553                  continue;
 554              }
 555              $answer_title = !empty($varequal->item(0)->nodeValue) ? $varequal->item(0)->nodeValue : '';
 556  
 557              $display_feedback = $xpath->query('xmlns:displayfeedback', $response_item);
 558  
 559              unset($feedbacks_identifiers);
 560  
 561              if (!empty($display_feedback)) {
 562  
 563                  foreach ($display_feedback as $feedback) {
 564  
 565                      $feedback_identifier = $feedback->getAttributeNode('linkrefid');
 566                      $feedback_identifier = !empty($feedback_identifier->nodeValue) ? $feedback_identifier->nodeValue : '';
 567  
 568                      if (!empty($feedback_identifier)) {
 569                          $feedbacks_identifiers[] = $feedback_identifier;
 570                      }
 571                  }
 572              }
 573  
 574              $feedback = '';
 575              $feedbacks_identifiers = empty($feedbacks_identifiers) ? '' : $feedbacks_identifiers;
 576  
 577              if (!empty($feedbacks_identifiers)) {
 578                  foreach ($feedbacks_identifiers as $feedback_identifier) {
 579                      $feedbacks = $xpath->query('//xmlns:item[@ident="' . $question_identifier . '"]/xmlns:itemfeedback[@ident="' . $feedback_identifier . '"]/xmlns:flow_mat/xmlns:material/xmlns:mattext');
 580                      $feedback .= !empty($feedbacks->item(0)->nodeValue) ? $feedbacks->item(0)->nodeValue . ' ' : '';
 581                  }
 582              }
 583  
 584              if (array_key_exists($answer_title, $correctanswersfib)) {
 585                  // Already a correct answer, just need the feedback for the correct answer.
 586                  $correctanswerfib[$answer_title]['feedback'] = $feedback;
 587              } else {
 588                  // Need to add an incorrect answer.
 589                  $last_answer_id++;
 590                  $incorrectanswersfib[] = array(
 591                      'id' => $last_answer_id,
 592                      'title' => $answer_title,
 593                      'score' => 0,
 594                      'feedback' => $feedback,
 595                      'case' => 0);
 596              }
 597          }
 598  
 599          $answers_fib = array_merge($correctanswersfib, $incorrectanswersfib);
 600          $answers_fib = empty($answers_fib) ? '' : $answers_fib;
 601  
 602          return $answers_fib;
 603      }
 604  
 605      private function get_answers_pattern_match ($question_identifier, $identifier, $assessment, &$last_answer_id) {
 606  
 607          $xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
 608  
 609          $answers_fib = array();
 610  
 611          $response_items = $xpath->query('//xmlns:item[@ident="' . $question_identifier . '"]/xmlns:resprocessing/xmlns:respcondition');
 612  
 613          foreach ($response_items as $response_item) {
 614  
 615              $setvar = $xpath->query('xmlns:setvar', $response_item);
 616              $setvar = is_object($setvar->item(0)) ? $setvar->item(0)->nodeValue : '';
 617  
 618              if ($setvar != '') {
 619  
 620                  $last_answer_id++;
 621  
 622                  $answer_title = $xpath->query('xmlns:conditionvar/xmlns:varequal[@respident="' . $identifier . '"]', $response_item);
 623                  $answer_title = !empty($answer_title->item(0)->nodeValue) ? $answer_title->item(0)->nodeValue : '';
 624  
 625                  if (empty($answer_title)) {
 626                      $answer_title = $xpath->query('xmlns:conditionvar/xmlns:varsubstring[@respident="' . $identifier . '"]', $response_item);
 627                      $answer_title = !empty($answer_title->item(0)->nodeValue) ? '*' . $answer_title->item(0)->nodeValue . '*' : '';
 628                  }
 629  
 630                  if (empty($answer_title)) {
 631                      $answer_title = '*';
 632                  }
 633  
 634              $case = $xpath->query('xmlns:conditionvar/xmlns:varequal/@case', $response_item);
 635              $case = is_object($case->item(0)) ? $case->item(0)->nodeValue : 'no'
 636                                      ;
 637              $case = strtolower($case) == 'yes' ? 1 :
 638                              0;
 639  
 640                  $display_feedback = $xpath->query('xmlns:displayfeedback', $response_item);
 641  
 642                  unset($feedbacks_identifiers);
 643  
 644                  if (!empty($display_feedback)) {
 645  
 646                      foreach ($display_feedback as $feedback) {
 647  
 648                          $feedback_identifier = $feedback->getAttributeNode('linkrefid');
 649                          $feedback_identifier = !empty($feedback_identifier->nodeValue) ? $feedback_identifier->nodeValue : '';
 650  
 651                          if (!empty($feedback_identifier)) {
 652                              $feedbacks_identifiers[] = $feedback_identifier;
 653                          }
 654                      }
 655                  }
 656  
 657                  $feedback = '';
 658                  $feedbacks_identifiers = empty($feedbacks_identifiers) ? '' : $feedbacks_identifiers;
 659  
 660                  if (!empty($feedbacks_identifiers)) {
 661                      foreach ($feedbacks_identifiers as $feedback_identifier) {
 662                          $feedbacks = $xpath->query('//xmlns:item[@ident="' . $question_identifier . '"]/xmlns:itemfeedback[@ident="' . $feedback_identifier . '"]/xmlns:flow_mat/xmlns:material/xmlns:mattext');
 663                          $feedback .= !empty($feedbacks->item(0)->nodeValue) ? $feedbacks->item(0)->nodeValue . ' ' : '';
 664                      }
 665                  }
 666  
 667                  $answers_fib[] = array('id' => $last_answer_id,
 668                                         'title' => $answer_title,
 669                                         'score' => $setvar,
 670                                         'feedback' => $feedback,
 671                                         'case' => $case);
 672              }
 673          }
 674  
 675          $answers_fib = empty($answers_fib) ? '' : $answers_fib;
 676  
 677          return $answers_fib;
 678      }
 679  
 680  
 681      private function get_answers ($identifier, $assessment, &$last_answer_id) {
 682  
 683          $xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
 684  
 685          $answers = array();
 686  
 687          $question_cc_type = $this->get_question_type($identifier, $assessment);
 688          $question_cc_type = $question_cc_type['cc'];
 689          $is_multiresponse = ($question_cc_type == CC_QUIZ_MULTIPLE_RESPONSE);
 690  
 691          if ($question_cc_type == CC_QUIZ_MULTIPLE_CHOICE || $is_multiresponse || $question_cc_type == CC_QUIZ_TRUE_FALSE) {
 692  
 693              $query_answers = '//xmlns:item[@ident="' . $identifier . '"]/xmlns:presentation/xmlns:response_lid/xmlns:render_choice/xmlns:response_label';
 694              $query_answers_with_flow = '//xmlns:item[@ident="' . $identifier . '"]/xmlns:presentation/xmlns:flow/xmlns:response_lid/xmlns:render_choice/xmlns:response_label';
 695  
 696              $query_indentifer = '@ident';
 697              $query_title = 'xmlns:material/xmlns:mattext';
 698          }
 699  
 700          if ($question_cc_type == CC_QUIZ_ESSAY) {
 701  
 702              $query_answers = '//xmlns:item[@ident="' . $identifier . '"]/xmlns:presentation/xmlns:response_str';
 703              $query_answers_with_flow = '//xmlns:item[@ident="' . $identifier . '"]/xmlns:presentation/xmlns:flow/xmlns:response_str';
 704  
 705              $query_indentifer = '@ident';
 706              $query_title = 'xmlns:render_fib';
 707          }
 708  
 709          if ($question_cc_type == CC_QUIZ_FIB || $question_cc_type == CC_QUIZ_PATTERN_MACHT) {
 710  
 711              $xpath_query = '//xmlns:item[@ident="' . $identifier . '"]/xmlns:presentation/xmlns:response_str/@ident';
 712              $xpath_query_with_flow = '//xmlns:item[@ident="' . $identifier . '"]/xmlns:presentation/xmlns:flow/xmlns:response_str/@ident';
 713  
 714              $count_response = $xpath->evaluate('count(' . $xpath_query_with_flow . ')');
 715  
 716              if ($count_response == 0) {
 717                  $answer_identifier = $xpath->query($xpath_query);
 718              } else {
 719                  $answer_identifier = $xpath->query($xpath_query_with_flow);
 720              }
 721  
 722              $answer_identifier = !empty($answer_identifier->item(0)->nodeValue) ? $answer_identifier->item(0)->nodeValue : '';
 723  
 724              if ($question_cc_type == CC_QUIZ_FIB) {
 725                  $answers = $this->get_answers_fib ($identifier, $answer_identifier, $assessment, $last_answer_id);
 726              } else {
 727                  $answers = $this->get_answers_pattern_match ($identifier, $answer_identifier, $assessment, $last_answer_id);
 728              }
 729  
 730          } else {
 731  
 732              $count_response = $xpath->evaluate('count(' . $query_answers_with_flow . ')');
 733  
 734              if ($count_response == 0) {
 735                  $response_items = $xpath->query($query_answers);
 736              } else {
 737                  $response_items = $xpath->query($query_answers_with_flow);
 738              }
 739  
 740              if (!empty($response_items)) {
 741  
 742                  if ($is_multiresponse) {
 743                      $correct_answer_score = 0;
 744                      //get the correct answers count
 745                      $canswers_query = "//xmlns:item[@ident='{$identifier}']//xmlns:setvar[@varname='SCORE'][.=100]/../xmlns:conditionvar//xmlns:varequal[@case='Yes'][not(parent::xmlns:not)]";
 746                      $canswers = $xpath->query($canswers_query);
 747                      if ($canswers->length > 0) {
 748                          $correct_answer_score = round(1.0 / (float)$canswers->length, 7); //weird
 749                          $correct_answers_ident = array();
 750                          foreach ($canswers as $cnode) {
 751                              $correct_answers_ident[$cnode->nodeValue] = true;
 752                          }
 753                      }
 754                  }
 755  
 756                  foreach ($response_items as $response_item) {
 757  
 758                      $last_answer_id++;
 759  
 760                      $answer_identifier = $xpath->query($query_indentifer, $response_item);
 761                      $answer_identifier = !empty($answer_identifier->item(0)->nodeValue) ? $answer_identifier->item(0)->nodeValue : '';
 762  
 763                      $answer_title = $xpath->query($query_title, $response_item);
 764                      $answer_title = !empty($answer_title->item(0)->nodeValue) ? $answer_title->item(0)->nodeValue : '';
 765  
 766                      $answer_feedback = $this->get_feedback($assessment, $answer_identifier, $identifier, $question_cc_type);
 767  
 768                      $answer_score = $this->get_score($assessment, $answer_identifier, $identifier);
 769  
 770                      if ($is_multiresponse && isset($correct_answers_ident[$answer_identifier])) {
 771                          $answer_score = $correct_answer_score;
 772                      }
 773  
 774                      $answers[] = array('id' => $last_answer_id,
 775                                         'title' => $answer_title,
 776                                         'score' => $answer_score,
 777                                         'identifier' => $answer_identifier,
 778                                         'feedback' => $answer_feedback);
 779                  }
 780              }
 781          }
 782  
 783          $answers = empty($answers) ? '' : $answers;
 784  
 785          return $answers;
 786  
 787      }
 788  
 789      private function get_score ($assessment, $identifier, $question_identifier) {
 790  
 791          $xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
 792  
 793          $resource_processing = $xpath->query('//xmlns:item[@ident="' . $question_identifier . '"]/xmlns:resprocessing/xmlns:respcondition');
 794  
 795          if (!empty($resource_processing)) {
 796  
 797              foreach ($resource_processing as $response) {
 798  
 799                  $question_cc_type = $this->get_question_type($question_identifier, $assessment);
 800                  $question_cc_type = $question_cc_type['cc'];
 801  
 802                  $varequal = $xpath->query('xmlns:conditionvar/xmlns:varequal', $response);
 803                  $varequal = !empty($varequal->item(0)->nodeValue) ? $varequal->item(0)->nodeValue : '';
 804  
 805                  if (strtolower($varequal) == strtolower($identifier)) {
 806                      $score = $xpath->query('xmlns:setvar', $response);
 807                      $score = !empty($score->item(0)->nodeValue) ? $score->item(0)->nodeValue : '';
 808                  }
 809              }
 810          }
 811  
 812          // This method (get_score) is only used by T/F & M/C questions in CC, therefore it's either 0 or 1 in Moodle.
 813          $score = empty($score) ? "0.0000000" : '1.0000000';
 814  
 815          return $score;
 816      }
 817  
 818      private function create_node_course_question_categories_question_category_question_multiple_choice ($question) {
 819  
 820          $node_course_question_categories_question_answer = '';
 821          $sheet_question_categories_question = cc112moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES_QUESTION_CATEGORY_QUESTION_MULTIPLE_CHOICE);
 822  
 823          if (!empty($question['answers'])) {
 824              foreach ($question['answers'] as $answer) {
 825                  $node_course_question_categories_question_answer .= $this->create_node_course_question_categories_question_category_question_answer($answer);
 826              }
 827          }
 828  
 829          $answer_string = $this->get_answers_string($question['answers']);
 830  
 831          $is_single = ($question['cc_type'] == CC_QUIZ_MULTIPLE_CHOICE) ? 1 : 0;
 832  
 833          $find_tags = array('[#node_course_question_categories_question_category_question_answer#]',
 834                             '[#answer_string#]',
 835                             '[#is_single#]');
 836  
 837          $replace_values = array($node_course_question_categories_question_answer,
 838                                  self::safexml($answer_string),
 839                                  $is_single);
 840  
 841          $node_question_categories_question = str_replace($find_tags, $replace_values, $sheet_question_categories_question);
 842  
 843          return $node_question_categories_question;
 844      }
 845  
 846      private function create_node_course_question_categories_question_category_question_eesay ($question) {
 847  
 848          $node_course_question_categories_question_answer = '';
 849  
 850          $sheet_question_categories_question = cc112moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES_QUESTION_CATEGORY_QUESTION_EESAY);
 851  
 852          if (!empty($question['answers'])) {
 853              foreach ($question['answers'] as $answer) {
 854                  $node_course_question_categories_question_answer .= $this->create_node_course_question_categories_question_category_question_answer($answer);
 855              }
 856          }
 857  
 858          $find_tags = array('[#node_course_question_categories_question_category_question_answer#]');
 859          $replace_values = array($node_course_question_categories_question_answer);
 860  
 861          $node_question_categories_question = str_replace($find_tags, $replace_values, $sheet_question_categories_question);
 862  
 863          return $node_question_categories_question;
 864      }
 865  
 866      private function create_node_course_question_categories_question_category_question_shortanswer ($question) { //, &$fib_questions) {
 867  
 868          $sheet_question_categories_question = cc112moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES_QUESTION_CATEGORY_QUESTION_SHORTANSWER);
 869          $node_course_question_categories_question_answer = '';
 870  
 871          if (!empty($question['answers'])) {
 872              foreach ($question['answers'] as $answer) {
 873                  $node_course_question_categories_question_answer .= $this->create_node_course_question_categories_question_category_question_answer($answer);
 874              }
 875          }
 876  
 877          $answers_string = $this->get_answers_string($question['answers']);
 878  
 879          $use_case = 0;
 880  
 881          foreach ($question['answers'] as $answer) {
 882  
 883              if ($answer['case'] == 1) {
 884                  $use_case = 1;
 885              }
 886  
 887          }
 888  
 889          $find_tags = array('[#answers_string#]',
 890                             '[#use_case#]',
 891                             '[#node_course_question_categories_question_category_question_answer#]');
 892  
 893          $replace_values = array(self::safexml($answers_string),
 894                                  self::safexml($use_case),
 895                                  $node_course_question_categories_question_answer);
 896  
 897  
 898  
 899          $node_question_categories_question = str_replace($find_tags, $replace_values, $sheet_question_categories_question);
 900  
 901          return $node_question_categories_question;
 902  
 903      }
 904  
 905      private function create_node_course_question_categories_question_category_question_true_false ($question) {
 906  
 907          $node_course_question_categories_question_answer = '';
 908  
 909          $sheet_question_categories_question = cc112moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES_QUESTION_CATEGORY_QUESTION_TRUE_FALSE);
 910  
 911          $trueanswer  = null;
 912          $falseanswer = null;
 913  
 914          if (!empty($question['answers'])) {
 915  
 916              // Identify the true and false answers.
 917              foreach ($question['answers'] as $answer) {
 918                  if ($answer['identifier'] == 'true') {
 919                      $trueanswer = $answer;
 920                  } else if ($answer['identifier'] == 'false') {
 921                      $falseanswer = $answer;
 922                  } else {
 923                      // Should not happen, but just in case.
 924                      throw new coding_exception("Unknown answer identifier detected " .
 925                              "in true/false quiz question with id {$question['id']}.");
 926                  }
 927  
 928                  $node_course_question_categories_question_answer .= $this->create_node_course_question_categories_question_category_question_answer($answer);
 929              }
 930  
 931              // Make sure the true and false answer was found.
 932              if (is_null($trueanswer) || is_null($falseanswer)) {
 933                  throw new coding_exception("Unable to correctly identify the " .
 934                          "true and false answers in the question with id {$question['id']}.");
 935              }
 936          }
 937  
 938          $find_tags = array('[#node_course_question_categories_question_category_question_answer#]',
 939                             '[#true_answer_id#]',
 940                             '[#false_answer_id#]');
 941  
 942          $replace_values = array($node_course_question_categories_question_answer,
 943                                  $trueanswer['id'],
 944                                  $falseanswer['id']);
 945  
 946          $node_question_categories_question = str_replace($find_tags, $replace_values, $sheet_question_categories_question);
 947  
 948          return $node_question_categories_question;
 949      }
 950  
 951      private function get_answers_string ($answers) {
 952  
 953          $answer_string = '';
 954  
 955          if (!empty($answers)) {
 956              foreach ($answers as $answer) {
 957                  $answer_string .= $answer['id'] . ',';
 958              }
 959          }
 960  
 961          $answer_string = !empty($answer_string) ? substr($answer_string, 0, strlen($answer_string) - 1) : '';
 962  
 963          return $answer_string;
 964  
 965      }
 966  
 967      private function create_node_course_question_categories_question_category_question_answer ($answer) {
 968  
 969          $sheet_question_categories_question_answer = cc112moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES_QUESTION_CATEGORY_QUESTION_ANSWER);
 970  
 971          $find_tags = array('[#answer_id#]',
 972                             '[#answer_text#]',
 973                             '[#answer_score#]',
 974                             '[#answer_feedback#]');
 975  
 976          $replace_values = array($answer['id'],
 977                                  self::safexml($answer['title']),
 978                                  $answer['score'],
 979                                  self::safexml($answer['feedback']));
 980  
 981          $node_question_categories_question_answer = str_replace($find_tags, $replace_values, $sheet_question_categories_question_answer);
 982  
 983          return $node_question_categories_question_answer;
 984      }
 985  
 986      private function get_question_type ($identifier, $assessment) {
 987  
 988          $xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
 989  
 990          $metadata = $xpath->query('//xmlns:item[@ident="' . $identifier . '"]/xmlns:itemmetadata/xmlns:qtimetadata/xmlns:qtimetadatafield');
 991  
 992          foreach ($metadata as $field) {
 993  
 994              $field_label = $xpath->query('xmlns:fieldlabel', $field);
 995              $field_label = !empty($field_label->item(0)->nodeValue) ? $field_label->item(0)->nodeValue : '';
 996  
 997              if ($field_label == 'cc_profile') {
 998                  $field_entry = $xpath->query('xmlns:fieldentry', $field);
 999                  $type = !empty($field_entry->item(0)->nodeValue) ? $field_entry->item(0)->nodeValue : '';
1000              }
1001          }
1002  
1003          $return_type = array();
1004  
1005          $return_type['moodle'] = '';
1006          $return_type['cc'] = $type;
1007  
1008          if ($type == CC_QUIZ_MULTIPLE_CHOICE) {
1009              $return_type['moodle'] = MOODLE_QUIZ_MULTIPLE_CHOICE;
1010          }
1011          if ($type == CC_QUIZ_MULTIPLE_RESPONSE) {
1012              $return_type['moodle'] = MOODLE_QUIZ_MULTIPLE_CHOICE;
1013          }
1014          if ($type == CC_QUIZ_TRUE_FALSE) {
1015              $return_type['moodle'] = MOODLE_QUIZ_TRUE_FALSE;
1016          }
1017          if ($type == CC_QUIZ_ESSAY) {
1018              $return_type['moodle'] = MOODLE_QUIZ_ESSAY;
1019          }
1020          if ($type == CC_QUIZ_FIB) {
1021              $return_type['moodle'] = MOODLE_QUIZ_SHORTANSWER;
1022          }
1023          if ($type == CC_QUIZ_PATTERN_MACHT) {
1024              $return_type['moodle'] = MOODLE_QUIZ_SHORTANSWER;
1025          }
1026  
1027          return $return_type;
1028  
1029      }
1030  }