[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/mod/hotpot/ -> lib.php (source)

   1  <?PHP  // $Id: lib.php,v 1.79.2.15 2008/07/24 21:58:05 skodak Exp $
   2  
   3  //////////////////////////////////
   4  /// CONFIGURATION settings
   5  
   6  if (!isset($CFG->hotpot_showtimes)) {
   7      set_config("hotpot_showtimes", 0);
   8  }
   9  if (!isset($CFG->hotpot_excelencodings)) {
  10      set_config("hotpot_excelencodings", "");
  11  }
  12  
  13  //////////////////////////////////
  14  /// CONSTANTS and GLOBAL VARIABLES
  15  
  16  $CFG->hotpotroot = "$CFG->dirroot/mod/hotpot";
  17  $CFG->hotpottemplate = "$CFG->hotpotroot/template";
  18  if (!empty($_SERVER['HTTP_USER_AGENT'])) {
  19      $CFG->hotpotismobile = preg_match('/Alcatel|ATTWS|DoCoMo|Doris|Hutc3G|J-PHONE|Java|KDDI|KGT|LGE|MOT|Nokia|portalmmm|ReqwirelessWeb|SAGEM|SHARP|SIE-|SonyEricsson|Teleport|UP\.Browser|UPG1|Wapagsim/', $_SERVER['HTTP_USER_AGENT']);
  20  } else {
  21      $CFG->hotpotismobile = false;
  22  }
  23  
  24  define("HOTPOT_JS", "$CFG->wwwroot/mod/hotpot/hotpot-full.js");
  25  
  26  define("HOTPOT_NO",  "0");
  27  define("HOTPOT_YES", "1");
  28  
  29  define ("HOTPOT_TEXTSOURCE_QUIZ", "0");
  30  define ("HOTPOT_TEXTSOURCE_FILENAME", "1");
  31  define ("HOTPOT_TEXTSOURCE_FILEPATH", "2");
  32  define ("HOTPOT_TEXTSOURCE_SPECIFIC", "3");
  33  
  34  define("HOTPOT_LOCATION_COURSEFILES", "0");
  35  define("HOTPOT_LOCATION_SITEFILES",   "1");
  36  
  37  $HOTPOT_LOCATION = array (
  38      HOTPOT_LOCATION_COURSEFILES => get_string("coursefiles"),
  39      HOTPOT_LOCATION_SITEFILES   => get_string("sitefiles"),
  40  );
  41  
  42  define("HOTPOT_OUTPUTFORMAT_BEST",     "1");
  43  define("HOTPOT_OUTPUTFORMAT_V3",      "10");
  44  define("HOTPOT_OUTPUTFORMAT_V4",      "11");
  45  define("HOTPOT_OUTPUTFORMAT_V5",      "12");
  46  define("HOTPOT_OUTPUTFORMAT_V5_PLUS", "13");
  47  define("HOTPOT_OUTPUTFORMAT_V6",      "14");
  48  define("HOTPOT_OUTPUTFORMAT_V6_PLUS", "15");
  49  define("HOTPOT_OUTPUTFORMAT_FLASH",   "20");
  50  define("HOTPOT_OUTPUTFORMAT_MOBILE",  "30");
  51  
  52  $HOTPOT_OUTPUTFORMAT = array (
  53      HOTPOT_OUTPUTFORMAT_BEST    => get_string("outputformat_best", "hotpot"),
  54      HOTPOT_OUTPUTFORMAT_V6_PLUS => get_string("outputformat_v6_plus", "hotpot"),
  55      HOTPOT_OUTPUTFORMAT_V6      => get_string("outputformat_v6", "hotpot"),
  56      HOTPOT_OUTPUTFORMAT_V5_PLUS => get_string("outputformat_v5_plus", "hotpot"),
  57      HOTPOT_OUTPUTFORMAT_V5      => get_string("outputformat_v5", "hotpot"),
  58      HOTPOT_OUTPUTFORMAT_V4      => get_string("outputformat_v4", "hotpot"),
  59      HOTPOT_OUTPUTFORMAT_V3      => get_string("outputformat_v3", "hotpot"),
  60      HOTPOT_OUTPUTFORMAT_FLASH   => get_string("outputformat_flash", "hotpot"),
  61      HOTPOT_OUTPUTFORMAT_MOBILE  => get_string("outputformat_mobile", "hotpot"),
  62  );
  63  $HOTPOT_OUTPUTFORMAT_DIR = array (
  64      HOTPOT_OUTPUTFORMAT_V6_PLUS => 'v6',
  65      HOTPOT_OUTPUTFORMAT_V6      => 'v6',
  66      HOTPOT_OUTPUTFORMAT_V5_PLUS => 'v5',
  67      HOTPOT_OUTPUTFORMAT_V5      => 'v5',
  68      HOTPOT_OUTPUTFORMAT_V4      => 'v4',
  69      HOTPOT_OUTPUTFORMAT_V3      => 'v3',
  70      HOTPOT_OUTPUTFORMAT_FLASH   => 'flash',
  71      HOTPOT_OUTPUTFORMAT_MOBILE  => 'mobile',
  72  );
  73  foreach ($HOTPOT_OUTPUTFORMAT_DIR as $format=>$dir) {
  74      if (is_file("$CFG->hotpottemplate/$dir.php") && is_dir("$CFG->hotpottemplate/$dir")) {
  75          // do nothing ($format is available)
  76      } else {
  77          // $format is not available, so remove it
  78          unset($HOTPOT_OUTPUTFORMAT[$format]);
  79          unset($HOTPOT_OUTPUTFORMAT_DIR[$format]);
  80      }
  81  }
  82  define("HOTPOT_NAVIGATION_BAR",     "1");
  83  define("HOTPOT_NAVIGATION_FRAME",   "2");
  84  define("HOTPOT_NAVIGATION_IFRAME",  "3");
  85  define("HOTPOT_NAVIGATION_BUTTONS", "4");
  86  define("HOTPOT_NAVIGATION_GIVEUP",  "5");
  87  define("HOTPOT_NAVIGATION_NONE",    "6");
  88  
  89  $HOTPOT_NAVIGATION = array (
  90      HOTPOT_NAVIGATION_BAR     => get_string("navigation_bar", "hotpot"),
  91      HOTPOT_NAVIGATION_FRAME   => get_string("navigation_frame", "hotpot"),
  92      HOTPOT_NAVIGATION_IFRAME  => get_string("navigation_iframe", "hotpot"),
  93      HOTPOT_NAVIGATION_BUTTONS => get_string("navigation_buttons", "hotpot"),
  94      HOTPOT_NAVIGATION_GIVEUP  => get_string("navigation_give_up", "hotpot"),
  95      HOTPOT_NAVIGATION_NONE    => get_string("navigation_none", "hotpot"),
  96  );
  97  
  98  define("HOTPOT_JCB",    "1");
  99  define("HOTPOT_JCLOZE", "2");
 100  define("HOTPOT_JCROSS", "3");
 101  define("HOTPOT_JMATCH", "4");
 102  define("HOTPOT_JMIX",   "5");
 103  define("HOTPOT_JQUIZ",  "6");
 104  define("HOTPOT_TEXTOYS_RHUBARB",   "7");
 105  define("HOTPOT_TEXTOYS_SEQUITUR",  "8");
 106  
 107  $HOTPOT_QUIZTYPE = array(
 108      HOTPOT_JCB    => 'JCB',
 109      HOTPOT_JCLOZE => 'JCloze',
 110      HOTPOT_JCROSS => 'JCross',
 111      HOTPOT_JMATCH => 'JMatch',
 112      HOTPOT_JMIX   => 'JMix',
 113      HOTPOT_JQUIZ  => 'JQuiz',
 114      HOTPOT_TEXTOYS_RHUBARB  => 'Rhubarb',
 115      HOTPOT_TEXTOYS_SEQUITUR => 'Sequitur'
 116  );
 117  
 118  define("HOTPOT_JQUIZ_MULTICHOICE", "1");
 119  define("HOTPOT_JQUIZ_SHORTANSWER", "2");
 120  define("HOTPOT_JQUIZ_HYBRID",      "3");
 121  define("HOTPOT_JQUIZ_MULTISELECT", "4");
 122  
 123  define("HOTPOT_GRADEMETHOD_HIGHEST", "1");
 124  define("HOTPOT_GRADEMETHOD_AVERAGE", "2");
 125  define("HOTPOT_GRADEMETHOD_FIRST",   "3");
 126  define("HOTPOT_GRADEMETHOD_LAST",    "4");
 127  
 128  $HOTPOT_GRADEMETHOD = array (
 129      HOTPOT_GRADEMETHOD_HIGHEST => get_string("gradehighest", "quiz"),
 130      HOTPOT_GRADEMETHOD_AVERAGE => get_string("gradeaverage", "quiz"),
 131      HOTPOT_GRADEMETHOD_FIRST   => get_string("attemptfirst", "quiz"),
 132      HOTPOT_GRADEMETHOD_LAST    => get_string("attemptlast",  "quiz"),
 133  );
 134  
 135  define("HOTPOT_STATUS_INPROGRESS", "1");
 136  define("HOTPOT_STATUS_TIMEDOUT",   "2");
 137  define("HOTPOT_STATUS_ABANDONED",  "3");
 138  define("HOTPOT_STATUS_COMPLETED",  "4");
 139  
 140  $HOTPOT_STATUS = array (
 141      HOTPOT_STATUS_INPROGRESS => get_string("inprogress", "hotpot"),
 142      HOTPOT_STATUS_TIMEDOUT   => get_string("timedout",   "hotpot"),
 143      HOTPOT_STATUS_ABANDONED  => get_string("abandoned",  "hotpot"),
 144      HOTPOT_STATUS_COMPLETED  => get_string("completed",  "hotpot"),
 145  );
 146  
 147  define("HOTPOT_FEEDBACK_NONE", "0");
 148  define("HOTPOT_FEEDBACK_WEBPAGE", "1");
 149  define("HOTPOT_FEEDBACK_FORMMAIL", "2");
 150  define("HOTPOT_FEEDBACK_MOODLEFORUM", "3");
 151  define("HOTPOT_FEEDBACK_MOODLEMESSAGING", "4");
 152  
 153  $HOTPOT_FEEDBACK = array (
 154      HOTPOT_FEEDBACK_NONE => get_string("feedbacknone", "hotpot"),
 155      HOTPOT_FEEDBACK_WEBPAGE => get_string("feedbackwebpage",  "hotpot"),
 156      HOTPOT_FEEDBACK_FORMMAIL => get_string("feedbackformmail", "hotpot"),
 157      HOTPOT_FEEDBACK_MOODLEFORUM => get_string("feedbackmoodleforum", "hotpot"),
 158      HOTPOT_FEEDBACK_MOODLEMESSAGING => get_string("feedbackmoodlemessaging", "hotpot"),
 159  );
 160  if (empty($CFG->messaging)) { // Moodle 1.4 (and less)
 161      unset($HOTPOT_FEEDBACK[HOTPOT_FEEDBACK_MOODLEMESSAGING]);
 162  }
 163  
 164  define("HOTPOT_DISPLAYNEXT_QUIZ",   "0");
 165  define("HOTPOT_DISPLAYNEXT_COURSE", "1");
 166  define("HOTPOT_DISPLAYNEXT_INDEX",  "2");
 167  
 168  /**
 169   * If start and end date for the quiz are more than this many seconds apart
 170   * they will be represented by two separate events in the calendar
 171   */
 172  define("HOTPOT_MAX_EVENT_LENGTH", "432000");   // 5 days maximum
 173  
 174  //////////////////////////////////
 175  /// CORE FUNCTIONS
 176  
 177  
 178  // possible return values:
 179  //    false:
 180  //        display moderr.html (if exists) OR "Could not update" and return to couse view
 181  //    string:
 182  //        display as error message and return to course view
 183  //  true (or non-zero number):
 184  //        continue to $hotpot->redirect (if set) OR hotpot/view.php (to displsay quiz)
 185  
 186  // $hotpot is an object containing the values of the form in mod.html
 187  // i.e. all the fields in the 'hotpot' table, plus the following:
 188  //  $hotpot->course       : an id in the 'course' table
 189  //  $hotpot->coursemodule : an id in the 'course_modules' table
 190  //  $hotpot->section      : an id in the 'course_sections' table
 191  //  $hotpot->module       : an id in the 'modules' table
 192  //  $hotpot->modulename   : always 'hotpot'
 193  //  $hotpot->instance     : an id in the 'hotpot' table
 194  //  $hotpot->mode         : 'add' or 'update'
 195  //  $hotpot->sesskey      : unique string required for Moodle's session management
 196  
 197  function hotpot_add_instance(&$hotpot) {
 198      if (hotpot_set_form_values($hotpot)) {
 199          if ($result = insert_record('hotpot', $hotpot)) {
 200              $hotpot->id = $result;
 201              hotpot_update_events($hotpot);
 202              hotpot_grade_item_update(stripslashes_recursive($hotpot));
 203          }
 204      } else {
 205          $result=  false;
 206      }
 207      return $result;
 208  }
 209  
 210  function hotpot_update_instance(&$hotpot) {
 211      if (hotpot_set_form_values($hotpot)) {
 212          $hotpot->id = $hotpot->instance;
 213          if ($result = update_record('hotpot', $hotpot)) {
 214              hotpot_update_events($hotpot);
 215              hotpot_grade_item_update(stripslashes_recursive($hotpot));
 216          }
 217      } else {
 218          $result=  false;
 219      }
 220      return $result;
 221  }
 222  function hotpot_update_events($hotpot) {
 223  
 224      // remove any previous calendar events for this hotpot
 225      delete_records('event', 'modulename', 'hotpot', 'instance', $hotpot->id);
 226  
 227      $event = new stdClass();
 228      $event->description = addslashes($hotpot->summary);
 229      $event->courseid    = $hotpot->course;
 230      $event->groupid     = 0;
 231      $event->userid      = 0;
 232      $event->modulename  = 'hotpot';
 233      $event->instance    = $hotpot->id;
 234      $event->timestart   = $hotpot->timeopen;
 235      if ($cm = get_coursemodule_from_id('hotpot', $hotpot->id)) {
 236          $event->visible = hotpot_is_visible($cm);
 237      } else {
 238          $event->visible = 1;
 239      }
 240  
 241      if ($hotpot->timeclose && $hotpot->timeopen) {
 242          // we have both a start and an end date
 243          $event->eventtype   = 'open';
 244          $event->timeduration = ($hotpot->timeclose - $hotpot->timeopen);
 245  
 246          if ($event->timeduration > HOTPOT_MAX_EVENT_LENGTH) {  /// Long durations create two events
 247  
 248              $event->name          = addslashes($hotpot->name).' ('.get_string('hotpotopens', 'hotpot').')';
 249              $event->timeduration  = 0;
 250              add_event($event);
 251  
 252              $event->timestart    = $hotpot->timeclose;
 253              $event->eventtype    = 'close';
 254              $event->name         = addslashes($hotpot->name).' ('.get_string('hotpotcloses', 'hotpot').')';
 255              unset($event->id);
 256              add_event($event);
 257          } else { // single event with duration
 258              $event->name        = $hotpot->name;
 259              add_event($event);
 260          }
 261      } elseif ($hotpot->timeopen) { // only an open date
 262          $event->name          = addslashes($hotpot->name).' ('.get_string('hotpotopens', 'hotpot').')';
 263          $event->eventtype   = 'open';
 264          $event->timeduration = 0;
 265          add_event($event);
 266      } elseif ($hotpot->timeclose) { // only a closing date
 267          $event->name         = addslashes($hotpot->name).' ('.get_string('hotpotcloses', 'hotpot').')';
 268          $event->timestart    = $hotpot->timeclose;
 269          $event->eventtype    = 'close';
 270          $event->timeduration = 0;
 271          add_event($event);
 272      }
 273  }
 274  
 275  function hotpot_set_form_values(&$hotpot) {
 276      $ok = true;
 277      $hotpot->errors = array(); // these will be reported by moderr.html
 278  
 279      if (empty($hotpot->reference)) {
 280          $ok = false;
 281          $hotpot->errors['reference']= get_string('error_nofilename', 'hotpot');
 282      }
 283  
 284      if (empty($hotpot->studentfeedbackurl) || $hotpot->studentfeedbackurl=='http://') {
 285          $hotpot->studentfeedbackurl = '';
 286          switch ($hotpot->studentfeedback) {
 287              case HOTPOT_FEEDBACK_WEBPAGE:
 288                  $ok = false;
 289                  $hotpot->errors['studentfeedbackurl']= get_string('error_nofeedbackurlwebpage', 'hotpot');
 290              break;
 291              case HOTPOT_FEEDBACK_FORMMAIL:
 292                  $ok = false;
 293                  $hotpot->errors['studentfeedbackurl']= get_string('error_nofeedbackurlformmail', 'hotpot');
 294              break;
 295          }
 296      }
 297  
 298      $time = time();
 299      $hotpot->timecreated = $time;
 300      $hotpot->timemodified = $time;
 301  
 302      if (empty($hotpot->mode)) {
 303          // moodle 1.9 (from mod_form.lib)
 304          if ($hotpot->add) {
 305              $hotpot->mode = 'add';
 306          } else if ($hotpot->update) {
 307              $hotpot->mode = 'update';
 308          } else {
 309              $hotpot->mode = '';
 310          }
 311      }
 312      if ($hotpot->quizchain==HOTPOT_YES) {
 313          switch ($hotpot->mode) {
 314              case 'add':
 315                  $ok = hotpot_add_chain($hotpot);
 316              break;
 317              case 'update':
 318                  $ok = hotpot_update_chain($hotpot);
 319              break;
 320          }
 321      } else { // $hotpot->quizchain==HOTPOT_NO
 322          hotpot_set_name_summary_reference($hotpot);
 323      }
 324  
 325      if (isset($hotpot->displaynext)) {
 326          switch ($hotpot->displaynext) {
 327              // N.B. redirection only works for Moodle 1.5+
 328              case HOTPOT_DISPLAYNEXT_COURSE:
 329                  $hotpot->redirect = true;
 330                  $hotpot->redirecturl = "view.php?id=$hotpot->course";
 331                  break;
 332              case HOTPOT_DISPLAYNEXT_INDEX:
 333                  $hotpot->redirect = true;
 334                  $hotpot->redirecturl = "../mod/hotpot/index.php?id=$hotpot->course";
 335                  break;
 336              default:
 337                  // use Moodle default action (i.e. go on to display the hotpot quiz)
 338          }
 339      } else {
 340          $hotpot->displaynext = HOTPOT_DISPLAYNEXT_QUIZ;
 341      }
 342  
 343      // if ($ok && $hotpot->setdefaults) {
 344      if ($ok) {
 345          set_user_preference('hotpot_timeopen', $hotpot->timeopen);
 346          set_user_preference('hotpot_timeclose', $hotpot->timeclose);
 347          set_user_preference('hotpot_navigation', $hotpot->navigation);
 348          set_user_preference('hotpot_outputformat', $hotpot->outputformat);
 349          set_user_preference('hotpot_studentfeedback', $hotpot->studentfeedback);
 350          set_user_preference('hotpot_studentfeedbackurl', $hotpot->studentfeedbackurl);
 351          set_user_preference('hotpot_forceplugins', $hotpot->forceplugins);
 352          set_user_preference('hotpot_shownextquiz', $hotpot->shownextquiz);
 353          set_user_preference('hotpot_review', $hotpot->review);
 354          set_user_preference('hotpot_grade', $hotpot->grade);
 355          set_user_preference('hotpot_grademethod', $hotpot->grademethod);
 356          set_user_preference('hotpot_attempts', $hotpot->attempts);
 357          set_user_preference('hotpot_subnet', $hotpot->subnet);
 358          set_user_preference('hotpot_displaynext', $hotpot->displaynext);
 359          if ($hotpot->mode=='add') {
 360              set_user_preference('hotpot_quizchain', $hotpot->quizchain);
 361              set_user_preference('hotpot_namesource', $hotpot->namesource);
 362              set_user_preference('hotpot_summarysource', $hotpot->summarysource);
 363          }
 364      }
 365      return $ok;
 366  }
 367  function hotpot_get_chain(&$cm) {
 368      // get details of course_modules in this section
 369      $course_module_ids = get_field('course_sections', 'sequence', 'id', $cm->section);
 370      if (empty($course_module_ids)) {
 371          $hotpot_modules = array();
 372      } else {
 373          $hotpot_modules = get_records_select('course_modules', "id IN ($course_module_ids) AND module=$cm->module");
 374          if (empty($hotpot_modules)) {
 375              $hotpot_modules = array();
 376          }
 377      }
 378  
 379      // get ids of hotpot modules in this section
 380      $ids = array();
 381      foreach ($hotpot_modules as $hotpot_module) {
 382          $ids[] = $hotpot_module->instance;
 383      }
 384  
 385      // get details of hotpots in this section
 386      if (empty($ids)) {
 387          $hotpots = array();
 388      } else {
 389          $hotpots = get_records_list('hotpot', 'id', implode(',', $ids));
 390      }
 391  
 392      $found = false;
 393      $chain = array();
 394  
 395      // loop through course_modules in this section
 396      $ids = explode(',', $course_module_ids);
 397      foreach ($ids as $id) {
 398  
 399          // check this course_module is a hotpot activity
 400          if (isset($hotpot_modules[$id])) {
 401  
 402              // store details of this course module and hotpot activity
 403              $hotpot_id = $hotpot_modules[$id]->instance;
 404              $chain[$id] = &$hotpot_modules[$id];
 405              $chain[$id]->hotpot = &$hotpots[$hotpot_id];
 406  
 407              // set $found, if this is the course module we're looking for
 408              if (isset($cm->coursemodule)) {
 409                  if ($id==$cm->coursemodule) {
 410                      $found = true;
 411                  }
 412              } else {
 413                  if ($id==$cm->id) {
 414                      $found = true;
 415                  }
 416              }
 417  
 418              // is this the end of a chain
 419              if (empty($hotpots[$hotpot_id]->shownextquiz)) {
 420                  if ($found) {
 421                      break; // out of loop
 422                  } else {
 423                      // restart chain (target cm has not been found yet)
 424                      $chain = array();
 425                  }
 426              }
 427          }
 428      } // end foreach $ids
 429  
 430      return $found ? $chain : false;
 431  }
 432  function hotpot_is_visible(&$cm) {
 433      global $CFG, $COURSE;
 434  
 435      // check grouping
 436      $modulecontext = get_context_instance(CONTEXT_MODULE, $cm->id);
 437      if (empty($CFG->enablegroupings) || empty($cm->groupmembersonly) || has_capability('moodle/site:accessallgroups', $modulecontext)) {
 438          // groupings not applicable
 439      } else if (!isguestuser() && groups_has_membership($cm)) {
 440          // user is in one of the groups in the allowed grouping
 441      } else {
 442          // user is not in the required grouping and does not have sufficiently privileges to view this hotpot activity
 443          return false;
 444      }
 445  
 446      // check if user can view hidden activities
 447      if (isset($COURSE->context)) {
 448          $coursecontext = &$COURSE->context;
 449      } else {
 450          $coursecontext = get_context_instance(CONTEXT_COURSE, $cm->course);
 451      }
 452      if (has_capability('moodle/course:viewhiddenactivities', $coursecontext)) {
 453          return true; // user can view hidden activities
 454      }
 455  
 456      if (!isset($cm->sectionvisible)) {
 457          if (! $section = get_record('course_sections', 'id', $cm->section)) {
 458              error('Course module record contains invalid section');
 459          }
 460          $cm->sectionvisible = $section->visible;
 461      }
 462      if (empty($cm->sectionvisible)) {
 463          $visible = HOTPOT_NO;
 464      } else {
 465          $visible = HOTPOT_YES;
 466          if (empty($cm->visible)) {
 467              if ($chain = hotpot_get_chain($cm)) {
 468                  $startofchain = array_shift($chain);
 469                  $visible = $startofchain->visible;
 470              }
 471          }
 472      }
 473      return $visible;
 474  }
 475  function hotpot_add_chain(&$hotpot) {
 476  /// add a chain of hotpot actiivities
 477  
 478      global $CFG, $course;
 479  
 480      $ok = true;
 481      $hotpot->names = array();
 482      $hotpot->summaries = array();
 483      $hotpot->references = array();
 484  
 485      $xml_quiz = new hotpot_xml_quiz($hotpot, false, false, false, false, false);
 486  
 487      if (isset($xml_quiz->error)) {
 488          $hotpot->errors['reference'] = $xml_quiz->error;
 489          $ok = false;
 490  
 491      } else if (is_dir($xml_quiz->filepath)) {
 492  
 493          // get list of hotpot files in this folder
 494          if ($dh = @opendir($xml_quiz->filepath)) {
 495              while (false !== ($file = @readdir($dh))) {
 496                  if (preg_match('/\.(jbc|jcl|jcw|jmt|jmx|jqz|htm|html)$/', $file)) {
 497                      $hotpot->references[] = "$xml_quiz->reference/$file";
 498                  }
 499              }
 500              closedir($dh);
 501  
 502              // get titles
 503              foreach ($hotpot->references as $i=>$reference) {
 504                  $filepath = $xml_quiz->fileroot.'/'.$reference;
 505                  hotpot_get_titles_and_next_ex($hotpot, $filepath);
 506                  $hotpot->names[$i] = $hotpot->exercisetitle;
 507                  $hotpot->summaries[$i] = $hotpot->exercisesubtitle;
 508              }
 509  
 510          } else {
 511              $ok = false;
 512              $hotpot->errors['reference'] = get_string('error_couldnotopenfolder', 'hotpot', $hotpot->reference);
 513          }
 514  
 515      } else if (is_file($xml_quiz->filepath)) {
 516  
 517          $filerootlength = strlen($xml_quiz->fileroot) + 1;
 518  
 519          while ($xml_quiz->filepath) {
 520              hotpot_get_titles_and_next_ex($hotpot, $xml_quiz->filepath, true);
 521              $hotpot->names[] = $hotpot->exercisetitle;
 522              $hotpot->summaries[] = $hotpot->exercisesubtitle;
 523              $hotpot->references[] = substr($xml_quiz->filepath, $filerootlength);
 524  
 525              if ($hotpot->nextexercise) {
 526                  $filepath = $xml_quiz->fileroot.'/'.$xml_quiz->filesubdir.$hotpot->nextexercise;
 527  
 528                  // check file is not already in chain
 529                  $reference = substr($filepath, $filerootlength);
 530                  if (in_array($reference, $hotpot->references)) {
 531                      $filepath = '';
 532                  }
 533              } else {
 534                  $filepath = '';
 535              }
 536              if ($filepath && file_exists($filepath) && is_file($filepath) && is_readable($filepath)) {
 537                  $xml_quiz->filepath = $filepath;
 538              } else {
 539                  $xml_quiz->filepath = false; // finish while loop
 540              }
 541          } // end while
 542  
 543      } else {
 544          $ok = false;
 545          $hotpot->errors['reference'] = get_string('error_notfileorfolder', 'hotpot', $hotpot->reference);
 546      }
 547  
 548      if (empty($hotpot->references) && empty($hotpot->errors['reference'])) {
 549          $ok = false;
 550          $hotpot->errors['reference'] = get_string('error_noquizzesfound', 'hotpot', $hotpot->reference);
 551      }
 552  
 553      if ($ok) {
 554          $hotpot->visible = HOTPOT_YES;
 555  
 556          if (trim($hotpot->name)=='') {
 557              $hotpot->name = get_string("modulename", $hotpot->modulename);
 558          }
 559          $hotpot->specificname = $hotpot->name;
 560          $hotpot->specificsummary = $hotpot->summary;
 561  
 562          // add all except last activity in chain
 563  
 564          $i_max = count($hotpot->references)-1;
 565          for ($i=0; $i<$i_max; $i++) {
 566  
 567              hotpot_set_name_summary_reference($hotpot, $i);
 568              $hotpot->reference = addslashes($hotpot->reference);
 569  
 570              if (!$hotpot->instance = insert_record("hotpot", $hotpot)) {
 571                  error("Could not add a new instance of $hotpot->modulename", "view.php?id=$hotpot->course");
 572              }
 573  
 574              // store (hotpot table) id of start of chain
 575              if ($i==0) {
 576                  $hotpot->startofchain = $hotpot->instance;
 577              }
 578  
 579              if (isset($course->groupmode)) {
 580                  $hotpot->groupmode = $course->groupmode;
 581              }
 582  
 583              if (! $hotpot->coursemodule = add_course_module($hotpot)) {
 584                  error("Could not add a new course module");
 585              }
 586              if (! $sectionid = add_mod_to_section($hotpot) ) {
 587                  error("Could not add the new course module to that section");
 588              }
 589  
 590              if (! set_field("course_modules", "section", $sectionid, "id", $hotpot->coursemodule)) {
 591                  error("Could not update the course module with the correct section");
 592              }
 593  
 594              add_to_log($hotpot->course, "course", "add mod",
 595                  "../mod/$hotpot->modulename/view.php?id=$hotpot->coursemodule",
 596                  "$hotpot->modulename $hotpot->instance"
 597              );
 598              add_to_log($hotpot->course, $hotpot->modulename, "add",
 599                  "view.php?id=$hotpot->coursemodule",
 600                  "$hotpot->instance", $hotpot->coursemodule
 601              );
 602  
 603              // hide tail of chain
 604              if ($hotpot->shownextquiz==HOTPOT_YES) {
 605                  $hotpot->visible = HOTPOT_NO;
 606              }
 607          } // end for ($hotpot->references)
 608  
 609          // settings for final activity in chain
 610          hotpot_set_name_summary_reference($hotpot, $i);
 611          $hotpot->reference = addslashes($hotpot->references[$i]);
 612          $hotpot->shownextquiz = HOTPOT_NO;
 613  
 614          if (isset($hotpot->startofchain)) {
 615              // redirection only works for Moodle 1.5+
 616              $hotpot->redirect = true;
 617              $hotpot->redirecturl = "$CFG->wwwroot/mod/hotpot/view.php?hp=$hotpot->startofchain";
 618          }
 619      } // end if $ok
 620  
 621      return $ok;
 622  }
 623  function hotpot_set_name_summary_reference(&$hotpot, $chain_index=NULL) {
 624  
 625      $xml_quiz = NULL;
 626  
 627      $textfields = array('name', 'summary');
 628      foreach ($textfields as $textfield) {
 629  
 630          $textsource = $textfield.'source';
 631  
 632          // are we adding a chain?
 633          if (isset($chain_index)) {
 634  
 635              switch ($hotpot->$textsource) {
 636                  case HOTPOT_TEXTSOURCE_QUIZ:
 637                      if ($textfield=='name') {
 638                          $hotpot->exercisetitle = $hotpot->names[$chain_index];
 639                      } else if ($textfield=='summary') {
 640                          $hotpot->exercisesubtitle = $hotpot->summaries[$chain_index];
 641                      }
 642                      break;
 643                  case HOTPOT_TEXTSOURCE_SPECIFIC:
 644                      $specifictext = 'specific'.$textfield;
 645                      if (empty($hotpot->$specifictext) && trim($hotpot->$specifictext)=='') {
 646                          $hotpot->$textfield = '';
 647                      } else {
 648                          $hotpot->$textfield = $hotpot->$specifictext.' ('.($chain_index+1).')';
 649                      }
 650                      break;
 651              }
 652              $hotpot->reference = $hotpot->references[$chain_index];
 653          }
 654  
 655          if ($hotpot->$textsource==HOTPOT_TEXTSOURCE_QUIZ) {
 656              if (empty($xml_quiz) && !isset($chain_index)) {
 657                  $xml_quiz = new hotpot_xml_quiz($hotpot, false, false, false, false, false);
 658                  hotpot_get_titles_and_next_ex($hotpot, $xml_quiz->filepath);
 659              }
 660              if ($textfield=='name') {
 661                  $hotpot->$textfield = addslashes($hotpot->exercisetitle);
 662              } else if ($textfield=='summary') {
 663                  $hotpot->$textfield = addslashes($hotpot->exercisesubtitle);
 664              }
 665          }
 666          switch ($hotpot->$textsource) {
 667              case HOTPOT_TEXTSOURCE_FILENAME:
 668                  $hotpot->$textfield = basename($hotpot->reference);
 669                  break;
 670              case HOTPOT_TEXTSOURCE_FILEPATH:
 671                  $hotpot->$textfield = '';
 672                  // continue to next lines
 673              default:
 674                  if (empty($hotpot->$textfield)) {
 675                      $hotpot->$textfield = str_replace('/', ' ', $hotpot->reference);
 676                  }
 677          } // end switch
 678      } // end foreach
 679  }
 680  function hotpot_get_titles_and_next_ex(&$hotpot, $filepath, $get_next=false) {
 681  
 682      $hotpot->exercisetitle = '';
 683      $hotpot->exercisesubtitle = '';
 684      $hotpot->nextexercise = '';
 685  
 686      // read the quiz file source
 687      if ($source = file_get_contents($filepath)) {
 688  
 689          $next = '';
 690          $title = '';
 691          $subtitle = '';
 692  
 693          if (preg_match('|\.html?$|', $filepath)) {
 694              // html file
 695              if (preg_match('|<h2[^>]*class="ExerciseTitle"[^>]*>(.*?)</h2>|is', $source, $matches)) {
 696                  $title = trim(strip_tags($matches[1]));
 697              }
 698              if (empty($title)) {
 699                  if (preg_match('|<title[^>]*>(.*?)</title>|is', $source, $matches)) {
 700                      $title = trim(strip_tags($matches[1]));
 701                  }
 702              }
 703              if (preg_match('|<h3[^>]*class="ExerciseSubtitle"[^>]*>(.*?)</h3>|is', $source, $matches)) {
 704                  $subtitle = trim(strip_tags($matches[1]));
 705              }
 706              if ($get_next) {
 707                  if (preg_match('|<div[^>]*class="NavButtonBar"[^>]*>(.*?)</div>|is', $source, $matches)) {
 708                      $navbuttonbar = $matches[1];
 709                      if (preg_match_all('|<button[^>]*class="NavButton"[^>]*onclick="'."location='([^']*)'".'[^"]*"[^>]*>|is', $navbuttonbar, $matches)) {
 710                          $lastbutton = count($matches[0])-1;
 711                          $next = $matches[1][$lastbutton];
 712                      }
 713                  }
 714              }
 715  
 716          } else {
 717              // xml file (...maybe)
 718              $xml_tree = new hotpot_xml_tree($source);
 719              $xml_tree->filetype = '';
 720  
 721              $keys = array_keys($xml_tree->xml);
 722              foreach ($keys as $key) {
 723                  if (preg_match('/^(hotpot|textoys)-(\w+)-file$/i', $key, $matches)) {
 724                      $xml_tree->filetype = 'xml';
 725                      $xml_tree->xml_root = "['$key']['#']";
 726                      $xml_tree->quiztype = strtolower($matches[2]);
 727                      break;
 728                  }
 729              }
 730              if ($xml_tree->filetype=='xml') {
 731  
 732                  $title = strip_tags($xml_tree->xml_value('data,title'));
 733                  $subtitle = $xml_tree->xml_value('hotpot-config-file,'.$xml_tree->quiztype.',exercise-subtitle');
 734  
 735                  if ($get_next) {
 736                      $include = $xml_tree->xml_value('hotpot-config-file,global,include-next-ex');
 737                      if (!empty($include)) {
 738                          $next = $xml_tree->xml_value("hotpot-config-file,$xml_tree->quiztype,next-ex-url");
 739                          if (is_array($next)) {
 740                              $next = $next[0]; // in case "next-ex-url" was repeated in the xml file
 741                          }
 742                      }
 743                  }
 744              }
 745          }
 746  
 747          $hotpot->nextexercise = $next;
 748          $hotpot->exercisetitle = (empty($title) || is_array($title)) ? basename($filepath) : $title;
 749          $hotpot->exercisesubtitle = (empty($subtitle) || is_array($subtitle)) ? $hotpot->exercisetitle : $subtitle;
 750      }
 751  }
 752  function hotpot_get_all_instances_in_course($modulename, $course) {
 753  /// called from index.php
 754  
 755      global $CFG;
 756      $instances = array();
 757  
 758      if (isset($CFG->release) && substr($CFG->release, 0, 3)>=1.2) {
 759          $groupmode = 'cm.groupmode,';
 760      } else {
 761          $groupmode = '';
 762      }
 763  
 764      $query = "
 765          SELECT
 766              cm.id AS coursemodule,
 767              cm.course AS course,
 768              cm.module AS module,
 769              cm.instance AS instance,
 770              -- cm.section AS section,
 771              cm.visible AS visible,
 772              $groupmode
 773              -- cs.section AS sectionnumber,
 774              cs.section AS section,
 775              cs.sequence AS sequence,
 776              cs.visible AS sectionvisible,
 777              thismodule.*
 778          FROM
 779              {$CFG->prefix}course_modules cm,
 780              {$CFG->prefix}course_sections cs,
 781              {$CFG->prefix}modules m,
 782              {$CFG->prefix}$modulename thismodule
 783          WHERE
 784              m.name = '$modulename' AND
 785              m.id = cm.module AND
 786              cm.course = '$course->id' AND
 787              cm.section = cs.id AND
 788              cm.instance = thismodule.id
 789      ";
 790      if ($rawmods = get_records_sql($query)) {
 791  
 792          // cache $isteacher setting
 793  
 794          $isteacher = has_capability('mod/hotpot:viewreport', get_context_instance(CONTEXT_COURSE, $course->id));
 795  
 796          $explodesection = array();
 797          $order = array();
 798  
 799          foreach ($rawmods as $rawmod) {
 800  
 801              if (empty($explodesection[$rawmod->section])) {
 802                  $explodesection[$rawmod->section] = true;
 803  
 804                  $coursemodules = explode(',', $rawmod->sequence);
 805                  foreach ($coursemodules as $i=>$coursemodule) {
 806                      $order[$coursemodule] = sprintf('%d.%04d', $rawmod->section, $i);
 807                  }
 808              }
 809  
 810              if ($isteacher) {
 811                  $visible = true;
 812              } else if ($modulename=='hotpot') {
 813                  $visible = hotpot_is_visible($rawmod);
 814              } else {
 815                  $visible = $rawmod->visible;
 816              }
 817  
 818              if ($visible) {
 819                  $instances[$order[$rawmod->coursemodule]] = $rawmod;
 820              }
 821  
 822          } // end foreach $modinfo
 823  
 824          ksort($instances);
 825          $instances = array_values($instances);
 826      }
 827  
 828      return $instances;
 829  }
 830  
 831  function hotpot_update_chain(&$hotpot) {
 832  /// update a chain of hotpot actiivities
 833  
 834      $ok = true;
 835      if ($hotpot_modules = hotpot_get_chain($hotpot)) {
 836  
 837          // skip updating of these fields
 838          $skipfields = array('id', 'course', 'name', 'reference', 'summary', 'shownextquiz');
 839          $fields = array();
 840  
 841          foreach ($hotpot_modules as $hotpot_module) {
 842  
 843              if ($hotpot->instance==$hotpot_module->id) {
 844                  // don't need to update this hotpot
 845  
 846              } else {
 847                  // shortcut to hotpot record
 848                  $thishotpot = &$hotpot_module->hotpot;
 849  
 850                  // get a list of fields to update (first time only)
 851                  if (empty($fields)) {
 852                      $fields = array_keys(get_object_vars($thishotpot));
 853                  }
 854  
 855                  // assume update is NOT required
 856                  $require_update = false;
 857  
 858                  // update field values (except $skipfields)
 859                  foreach($fields as $field) {
 860                      if (in_array($field, $skipfields) || $thishotpot->$field==$hotpot->$field) {
 861                          // update not required for this field
 862                      } else {
 863                          $require_update = true;
 864                          $thishotpot->$field = $hotpot->$field;
 865                      }
 866                  }
 867  
 868                  // update $thishotpot, if required
 869                  if ($require_update && !update_record("hotpot", $thishotpot)) {
 870                      error("Could not update the $hotpot->modulename", "view.php?id=$hotpot->course");
 871                  }
 872              }
 873          } // end foreach $ids
 874      }
 875      return $ok;
 876  }
 877  function hotpot_delete_instance($id) {
 878  /// Given an ID of an instance of this module,
 879  /// this function will permanently delete the instance
 880  /// and any data that depends on it.
 881  
 882      if (! $hotpot = get_record("hotpot", "id", $id)) {
 883          return false;
 884      }
 885  
 886      if (! delete_records("hotpot", "id", "$id")) {
 887          return false;
 888      }
 889  
 890      delete_records("hotpot_questions", "hotpot", "$id");
 891      if ($attempts = get_records_select("hotpot_attempts", "hotpot='$id'")) {
 892          $ids = implode(',', array_keys($attempts));
 893          delete_records_select("hotpot_attempts",  "id IN ($ids)");
 894          delete_records_select("hotpot_details",   "attempt IN ($ids)");
 895          delete_records_select("hotpot_responses", "attempt IN ($ids)");
 896      }
 897  
 898       // remove calendar events for this hotpot
 899      delete_records('event', 'modulename', 'hotpot', 'instance', $id);
 900  
 901       // remove grade item for this hotpot
 902      hotpot_grade_item_delete($hotpot);
 903  
 904      return true;
 905  }
 906  function hotpot_delete_and_notify($table, $select, $strtable) {
 907      $count = max(0, count_records_select($table, $select));
 908      if ($count) {
 909          delete_records_select($table, $select);
 910          $count -= max(0, count_records_select($table, $select));
 911          if ($count) {
 912              notify(get_string('deleted')." $count x $strtable");
 913          }
 914      }
 915  }
 916  
 917  function hotpot_user_complete($course, $user, $mod, $hotpot) {
 918  /// Print a detailed representation of what a  user has done with
 919  /// a given particular instance of this module, for user activity reports.
 920  
 921      $report = hotpot_user_outline($course, $user, $mod, $hotpot);
 922      if (empty($report)) {
 923          print get_string("noactivity", "hotpot");
 924      } else {
 925          $date = userdate($report->time, get_string('strftimerecentfull'));
 926          print $report->info.' '.get_string('mostrecently').': '.$date;
 927      }
 928      return true;
 929  }
 930  
 931  function hotpot_user_outline($course, $user, $mod, $hotpot) {
 932  /// Return a small object with summary information about what a
 933  /// user has done with a given particular instance of this module
 934  /// Used for user activity reports.
 935  /// $report->time = the time they did it
 936  /// $report->info = a short text description
 937  
 938      $report = NULL;
 939      if ($records = get_records_select("hotpot_attempts", "hotpot='$hotpot->id' AND userid='$user->id'", "timestart ASC", "*")) {
 940          $report = new stdClass();
 941          $scores = array();
 942          foreach ($records as $record){
 943              if (empty($report->time)) {
 944                  $report->time = $record->timestart;
 945              }
 946              $scores[] = hotpot_format_score($record);
 947          }
 948          if (empty($scores)) {
 949              $report->time = 0;
 950              $report->info = get_string('noactivity', 'hotpot');
 951          } else {
 952              $report->info = get_string('score', 'quiz').': '.implode(', ', $scores);
 953          }
 954      }
 955      return $report;
 956  }
 957  
 958  function hotpot_format_score($record, $undefined='&nbsp;') {
 959      if (isset($record->score)) {
 960          $score = $record->score;
 961      } else {
 962          $score = $undefined;
 963      }
 964      return $score;
 965  }
 966  
 967  function hotpot_format_status($record, $undefined='&nbsp;') {
 968      global $HOTPOT_STATUS;
 969  
 970      if (isset($record->status) || isset($HOTPOT_STATUS[$record->status])) {
 971          $status = $HOTPOT_STATUS[$record->status];
 972      } else {
 973          $status = $undefined;
 974      }
 975      return $status;
 976  }
 977  
 978  function hotpot_print_recent_activity($course, $isteacher, $timestart) {
 979  /// Given a course and a time, this module should find recent activity
 980  /// that has occurred in hotpot activities and print it out.
 981  /// Return true if there was output, or false is there was none.
 982  
 983      global $CFG;
 984      $result = false;
 985  
 986      $records = get_records_sql("
 987          SELECT
 988              h.id AS id,
 989              h.name AS name,
 990              COUNT(*) AS count_attempts
 991          FROM
 992              {$CFG->prefix}hotpot h,
 993              {$CFG->prefix}hotpot_attempts a
 994          WHERE
 995              h.course = $course->id
 996              AND h.id = a.hotpot
 997              AND a.id = a.clickreportid
 998              AND a.starttime > $timestart
 999          GROUP BY
1000              h.id, h.name
1001      ");
1002      // note that PostGreSQL requires h.name in the GROUP BY clause
1003  
1004      if($records) {
1005          $names = array();
1006          foreach ($records as $id => $record){
1007              if ($cm = get_coursemodule_from_instance('hotpot', $record->id, $course->id)) {
1008                  $context = get_context_instance(CONTEXT_MODULE, $cm->id);
1009  
1010                  if (has_capability('mod/hotpot:viewreport', $context)) {
1011                      $href = "$CFG->wwwroot/mod/hotpot/view.php?hp=$id";
1012                      $name = '&nbsp;<a href="'.$href.'">'.$record->name.'</a>';
1013                      if ($record->count_attempts > 1) {
1014                          $name .= " ($record->count_attempts)";
1015                      }
1016                      $names[] = $name;
1017                  }
1018              }
1019          }
1020          if (count($names) > 0) {
1021              print_headline(get_string('modulenameplural', 'hotpot').':');
1022  
1023              if ($CFG->version >= 2005050500) { // Moodle 1.5+
1024                  echo '<div class="head"><div class="name">'.implode('<br />', $names).'</div></div>';
1025              } else { // Moodle 1.4.x (or less)
1026                  echo '<font size="1">'.implode('<br />', $names).'</font>';
1027              }
1028              $result = true;
1029          }
1030      }
1031      return $result;  //  True if anything was printed, otherwise false
1032  }
1033  
1034  function hotpot_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $cmid="", $userid="", $groupid="") {
1035  // Returns all quizzes since a given time.
1036  
1037      global $CFG;
1038  
1039      // If $cmid or $userid are specified, then this restricts the results
1040      $cm_select = empty($cmid) ? "" : " AND cm.id = '$cmid'";
1041      $user_select = empty($userid) ? "" : " AND u.id = '$userid'";
1042  
1043      $records = get_records_sql("
1044          SELECT
1045              a.*,
1046              h.name, h.course,
1047              cm.instance, cm.section,
1048              u.firstname, u.lastname, u.picture
1049          FROM
1050              {$CFG->prefix}hotpot_attempts a,
1051              {$CFG->prefix}hotpot h,
1052              {$CFG->prefix}course_modules cm,
1053              {$CFG->prefix}user u
1054          WHERE
1055              a.timefinish > '$sincetime'
1056              AND a.id = a.clickreportid
1057              AND a.userid = u.id $user_select
1058              AND a.hotpot = h.id $cm_select
1059              AND cm.instance = h.id
1060              AND cm.course = '$courseid'
1061              AND h.course = cm.course
1062          ORDER BY
1063              a.timefinish ASC
1064      ");
1065  
1066      if (!empty($records)) {
1067          foreach ($records as $record) {
1068              if (empty($groupid) || groups_is_member($groupid, $record->userid)) {
1069  
1070                  unset($activity);
1071  
1072                  $activity->type = "hotpot";
1073                  $activity->defaultindex = $index;
1074                  $activity->instance = $record->hotpot;
1075  
1076                  $activity->name = $record->name;
1077                  $activity->section = $record->section;
1078  
1079                  $activity->content->attemptid = $record->id;
1080                  $activity->content->attempt = $record->attempt;
1081                  $activity->content->score = $record->score;
1082                  $activity->content->timestart = $record->timestart;
1083                  $activity->content->timefinish = $record->timefinish;
1084  
1085                  $activity->user->userid = $record->userid;
1086                  $activity->user->fullname = fullname($record);
1087                  $activity->user->picture = $record->picture;
1088  
1089                  $activity->timestamp = $record->timefinish;
1090  
1091                  $activities[] = $activity;
1092  
1093                  $index++;
1094              }
1095          } // end foreach
1096      }
1097  }
1098  
1099  function hotpot_print_recent_mod_activity($activity, $course, $detail=false) {
1100  /// Basically, this function prints the results of "hotpot_get_recent_activity"
1101  
1102      global $CFG, $THEME, $USER;
1103  
1104      if (isset($THEME->cellcontent2)) {
1105          $bgcolor =  ' bgcolor="'.$THEME->cellcontent2.'"';
1106      } else {
1107          $bgcolor = '';
1108      }
1109  
1110      print '<table border="0" cellpadding="3" cellspacing="0">';
1111  
1112      print '<tr><td'.$bgcolor.' class="forumpostpicture" width="35" valign="top">';
1113      print_user_picture($activity->user->userid, $course, $activity->user->picture);
1114      print '</td><td width="100%"><font size="2">';
1115  
1116      if ($detail) {
1117          // activity icon
1118          $src = "$CFG->modpixpath/$activity->type/icon.gif";
1119          print '<img src="'.$src.'" class="icon" alt="'.$activity->type.'" /> ';
1120  
1121          // link to activity
1122          $href = "$CFG->wwwroot/mod/hotpot/view.php?hp=$activity->instance";
1123          print '<a href="'.$href.'">'.$activity->name.'</a> - ';
1124      }
1125      if (has_capability('mod/hotpot:viewreport',get_context_instance(CONTEXT_COURSE, $course))) {
1126          // score (with link to attempt details)
1127          $href = "$CFG->wwwroot/mod/hotpot/review.php?hp=$activity->instance&attempt=".$activity->content->attemptid;
1128          print '<a href="'.$href.'">('.hotpot_format_score($activity->content).')</a> ';
1129  
1130          // attempt number
1131          print get_string('attempt', 'quiz').' - '.$activity->content->attempt.'<br />';
1132      }
1133  
1134      // link to user
1135      $href = "$CFG->wwwroot/user/view.php?id=$activity->user->userid&course=$course";
1136      print '<a href="'.$href.'">'.$activity->user->fullname.'</a> ';
1137  
1138      // time and date
1139      print ' - ' . userdate($activity->timestamp);
1140  
1141      // duration
1142      $duration = format_time($activity->content->timestart - $activity->content->timefinish);
1143      print " &nbsp; ($duration)";
1144  
1145      print "</font></td></tr>";
1146      print "</table>";
1147  }
1148  
1149  function hotpot_cron () {
1150  /// Function to be run periodically according to the moodle cron
1151  /// This function searches for things that need to be done, such
1152  /// as sending out mail, toggling flags etc ...
1153  
1154      global $CFG;
1155  
1156      return true;
1157  }
1158  
1159  function hotpot_grades($hotpotid) {
1160  /// Must return an array of grades for a given instance of this module,
1161  /// indexed by user.  It also returns a maximum allowed grade.
1162  
1163      $hotpot = get_record('hotpot', 'id', $hotpotid);
1164      $return->grades = hotpot_get_grades($hotpot);
1165      $return->maxgrade = $hotpot->grade;
1166  
1167      return $return;
1168  }
1169  function hotpot_get_grades($hotpot, $user_ids='') {
1170      global $CFG;
1171  
1172      $grades = array();
1173  
1174      $weighting = $hotpot->grade / 100;
1175      $precision = hotpot_get_precision($hotpot);
1176  
1177      // set the SQL string to determine the $grade
1178      $grade = "";
1179      switch ($hotpot->grademethod) {
1180          case HOTPOT_GRADEMETHOD_HIGHEST:
1181              $grade = "ROUND(MAX(score) * $weighting, $precision) AS grade";
1182              break;
1183          case HOTPOT_GRADEMETHOD_AVERAGE:
1184              // the 'AVG' function skips abandoned quizzes, so use SUM(score)/COUNT(id)
1185              $grade = "ROUND(SUM(score)/COUNT(id) * $weighting, $precision) AS grade";
1186              break;
1187          case HOTPOT_GRADEMETHOD_FIRST:
1188              $grade = "ROUND(score * $weighting, $precision)";
1189              $grade = sql_concat('timestart', "'_'", $grade);
1190              $grade = "MIN($grade) AS grade";
1191              break;
1192          case HOTPOT_GRADEMETHOD_LAST:
1193              $grade = "ROUND(score * $weighting, $precision)";
1194              $grade = sql_concat('timestart', "'_'", $grade);
1195              $grade = "MAX($grade) AS grade";
1196              break;
1197      }
1198  
1199      if ($grade) {
1200          $userid_condition = empty($user_ids) ? '' : "AND userid IN ($user_ids) ";
1201          $grades = get_records_sql_menu("
1202              SELECT userid, $grade
1203              FROM {$CFG->prefix}hotpot_attempts
1204              WHERE timefinish>0 AND hotpot='$hotpot->id' $userid_condition
1205              GROUP BY userid
1206          ");
1207          if ($grades) {
1208              if ($hotpot->grademethod==HOTPOT_GRADEMETHOD_FIRST || $hotpot->grademethod==HOTPOT_GRADEMETHOD_LAST) {
1209                  // remove left hand characters in $grade (up to and including the underscore)
1210                  foreach ($grades as $userid=>$grade) {
1211                      $grades[$userid] = substr($grades[$userid], strpos($grades[$userid], '_')+1);
1212                  }
1213              }
1214          }
1215      }
1216  
1217      return $grades;
1218  }
1219  function hotpot_get_precision(&$hotpot) {
1220      return ($hotpot->grademethod==HOTPOT_GRADEMETHOD_AVERAGE || $hotpot->grade<100) ? 1 : 0;
1221  }
1222  
1223  /**
1224   * Return grade for given user or all users.
1225   *
1226   * @param object $hotpot
1227   * @param int $userid optional user id, 0 means all users
1228   * @return array array of grades, false if none
1229   */
1230  function hotpot_get_user_grades($hotpot, $userid=0) {
1231      $grades = array();
1232      if ($hotpotgrades = hotpot_get_grades($hotpot, $userid)) {
1233          foreach ($hotpotgrades as $hotpotuserid => $hotpotgrade) {
1234              $grades[$hotpotuserid] = new stdClass();
1235              $grades[$hotpotuserid]->id        = $hotpotuserid;
1236              $grades[$hotpotuserid]->userid    = $hotpotuserid;
1237              $grades[$hotpotuserid]->rawgrade  = $hotpotgrade;
1238          }
1239      }
1240      if (count($grades)) {
1241          return $grades;
1242      } else {
1243          return false;
1244      }
1245  }
1246  
1247  /**
1248   * Update grades in central gradebook
1249   * this function is called from db/upgrade.php
1250   *     it is initially called with no arguments, which forces it to get a list of all hotpots
1251   *     it then iterates through the hotpots, calling itself to create a grade record for each hotpot
1252   *
1253   * @param object $hotpot null means all hotpots
1254   * @param int $userid specific user only, 0 means all users
1255   */
1256  function hotpot_update_grades($hotpot=null, $userid=0, $nullifnone=true) {
1257      global $CFG;
1258      if (! function_exists('grade_update')) {
1259          require_once($CFG->libdir.'/gradelib.php');
1260      }
1261      if (is_null($hotpot)) {
1262          // update (=create) grades for all hotpots
1263          $sql = "
1264              SELECT h.*, cm.idnumber as cmidnumber
1265              FROM {$CFG->prefix}hotpot h, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
1266              WHERE m.name='hotpot' AND m.id=cm.module AND cm.instance=h.id"
1267          ;
1268          if ($rs = get_recordset_sql($sql)) {
1269              while ($hotpot = rs_fetch_next_record($rs)) {
1270                  hotpot_update_grades($hotpot, 0, false);
1271              }
1272              rs_close($rs);
1273          }
1274      } else {
1275          // update (=create) grade for a single hotpot
1276          if ($grades = hotpot_get_user_grades($hotpot, $userid)) {
1277              hotpot_grade_item_update($hotpot, $grades);
1278  
1279          } else if ($userid && $nullifnone) {
1280              // no grades for this user, but we must force the creation of a "null" grade record
1281              $grade = new object();
1282              $grade->userid   = $userid;
1283              $grade->rawgrade = null;
1284              hotpot_grade_item_update($hotpot, $grade);
1285  
1286          } else {
1287              // no grades and no userid
1288              hotpot_grade_item_update($hotpot);
1289          }
1290      }
1291  }
1292  
1293  /**
1294   * Update/create grade item for given hotpot
1295   *
1296   * @param object $hotpot object with extra cmidnumber
1297   * @param mixed optional array/object of grade(s); 'reset' means reset grades in gradebook
1298   * @return object grade_item
1299   */
1300  function hotpot_grade_item_update($hotpot, $grades=null) {
1301      global $CFG;
1302      if (! function_exists('grade_update')) {
1303          require_once($CFG->libdir.'/gradelib.php');
1304      }
1305      $params = array('itemname' => $hotpot->name);
1306      if (array_key_exists('cmidnumber', $hotpot)) {
1307          //cmidnumber may not be always present
1308          $params['idnumber'] = $hotpot->cmidnumber;
1309      }
1310      if ($hotpot->grade > 0) {
1311          $params['gradetype'] = GRADE_TYPE_VALUE;
1312          $params['grademax']  = $hotpot->grade;
1313          $params['grademin']  = 0;
1314  
1315      } else {
1316          $params['gradetype'] = GRADE_TYPE_NONE;
1317      }
1318      return grade_update('mod/hotpot', $hotpot->course, 'mod', 'hotpot', $hotpot->id, 0, $grades, $params);
1319  }
1320  
1321  /**
1322   * Delete grade item for given hotpot
1323   *
1324   * @param object $hotpot object
1325   * @return object grade_item
1326   */
1327  function hotpot_grade_item_delete($hotpot) {
1328      global $CFG;
1329      if (! function_exists('grade_update')) {
1330          require_once($CFG->libdir.'/gradelib.php');
1331      }
1332      return grade_update('mod/hotpot', $hotpot->course, 'mod', 'hotpot', $hotpot->id, 0, null, array('deleted'=>1));
1333  }
1334  
1335  function hotpot_get_participants($hotpotid) {
1336  //Must return an array of user ids who are participants
1337  //for a given instance of hotpot. Must include every user involved
1338  //in the instance, independient of his role (student, teacher, admin...)
1339  //See other modules as example.
1340      global $CFG;
1341  
1342      return get_records_sql("
1343          SELECT DISTINCT
1344              u.id, u.id
1345          FROM
1346              {$CFG->prefix}user u,
1347              {$CFG->prefix}hotpot_attempts a
1348          WHERE
1349              u.id = a.userid
1350              AND a.hotpot = '$hotpotid'
1351      ");
1352  }
1353  
1354  function hotpot_scale_used ($hotpotid, $scaleid) {
1355  //This function returns if a scale is being used by one hotpot
1356  //it it has support for grading and scales. Commented code should be
1357  //modified if necessary. See forum, glossary or journal modules
1358  //as reference.
1359  
1360      $report = false;
1361  
1362      //$rec = get_record("hotpot","id","$hotpotid","scale","-$scaleid");
1363      //
1364      //if (!empty($rec)  && !empty($scaleid)) {
1365      //  $report = true;
1366      //}
1367  
1368      return $report;
1369  }
1370  
1371  /**
1372   * Checks if scale is being used by any instance of hotpot
1373   *
1374   * This is used to find out if scale used anywhere
1375   * @param $scaleid int
1376   * @return boolean True if the scale is used by any hotpot
1377   */
1378  function hotpot_scale_used_anywhere($scaleid) {
1379   return false;
1380  }
1381  
1382  //////////////////////////////////////////////////////////
1383  /// Any other hotpot functions go here.
1384  /// Each of them must have a name that starts with hotpot
1385  
1386  
1387  function hotpot_add_attempt($hotpotid) {
1388      global $db, $CFG, $USER;
1389  
1390      // get start time of this attempt
1391      $time = time();
1392  
1393      // set all previous "in progress" attempts at this quiz to "abandoned"
1394      if ($attempts = get_records_select('hotpot_attempts', "hotpot='$hotpotid' AND userid='$USER->id' AND status='".HOTPOT_STATUS_INPROGRESS."'")) {
1395          foreach ($attempts as $attempt) {
1396              if ($attempt->timefinish==0) {
1397                  $attempt->timefinish = $time;
1398              }
1399              if ($attempt->clickreportid==0) {
1400                  $attempt->clickreportid = $attempt->id;
1401              }
1402              $attempt->status = HOTPOT_STATUS_ABANDONED;
1403              update_record('hotpot_attempts', $attempt);
1404          }
1405      }
1406  
1407      // create and add new attempt record
1408      $attempt = new stdClass();
1409      $attempt->hotpot = $hotpotid;
1410      $attempt->userid = $USER->id;
1411      $attempt->attempt = hotpot_get_next_attempt($hotpotid);
1412      $attempt->timestart = $time;
1413  
1414      return insert_record("hotpot_attempts", $attempt);
1415  }
1416  function hotpot_get_next_attempt($hotpotid) {
1417      global $USER;
1418  
1419      // get max attempt so far
1420      $i = count_records_select('hotpot_attempts', "hotpot='$hotpotid' AND userid='$USER->id'", 'MAX(attempt)');
1421  
1422      return empty($i) ? 1 : ($i+1);
1423  }
1424  function hotpot_get_question_name($question) {
1425      $name = '';
1426      if (isset($question->text)) {
1427          $name = hotpot_strings($question->text);
1428      }
1429      if (empty($name)) {
1430          $name = $question->name;
1431      }
1432      return $name;
1433  }
1434  function hotpot_strings($ids) {
1435  
1436      // array of ids of empty strings
1437      static $HOTPOT_EMPTYSTRINGS;
1438  
1439      if (!isset($HOTPOT_EMPTYSTRINGS)) { // first time only
1440          // get ids of empty strings
1441          $emptystrings = get_records_select('hotpot_strings', 'LENGTH(TRIM(string))=0');
1442          $HOTPOT_EMPTYSTRINGS = empty($emptystrings) ? array() : array_keys($emptystrings);
1443      }
1444  
1445      $strings = array();
1446      if (!empty($ids)) {
1447          $ids = explode(',', $ids);
1448          foreach ($ids as $id) {
1449              if (!in_array($id, $HOTPOT_EMPTYSTRINGS)) {
1450                  $strings[] = hotpot_string($id);
1451              }
1452          }
1453      }
1454      return implode(',', $strings);
1455  }
1456  function hotpot_string($id) {
1457      return get_field('hotpot_strings', 'string', 'id', $id);
1458  }
1459  
1460  //////////////////////////////////////////////////////////////////////////////////////
1461  /// the class definitions to handle XML trees
1462  
1463  // get the standard XML parser supplied with Moodle
1464  require_once("$CFG->libdir/xmlize.php");
1465  
1466  // get the default class for hotpot quiz templates
1467  require_once("$CFG->hotpottemplate/default.php");
1468  
1469  class hotpot_xml_tree {
1470      function hotpot_xml_tree($str, $xml_root='') {
1471          if (empty($str)) {
1472              $this->xml =  array();
1473          } else {
1474              if (empty($CFG->unicodedb)) {
1475                  $str = utf8_encode($str);
1476              }
1477              $this->xml =  xmlize($str, 0);
1478          }
1479          $this->xml_root = $xml_root;
1480      }
1481      function xml_value($tags, $more_tags="[0]['#']") {
1482  
1483          $tags = empty($tags) ? '' : "['".str_replace(",", "'][0]['#']['", $tags)."']";
1484          eval('$value = &$this->xml'.$this->xml_root.$tags.$more_tags.';');
1485  
1486          if (is_string($value)) {
1487              if (empty($CFG->unicodedb)) {
1488                  $value = utf8_decode($value);
1489              }
1490  
1491              // decode angle brackets
1492              $value = strtr($value, array('&#x003C;'=>'<', '&#x003E;'=>'>', '&#x0026;'=>'&'));
1493  
1494              // remove white space between <table>, <ul|OL|DL> and <OBJECT|EMBED> parts
1495              // (so it doesn't get converted to <br />)
1496              $htmltags = '('
1497              .   'TABLE|/?CAPTION|/?COL|/?COLGROUP|/?TBODY|/?TFOOT|/?THEAD|/?TD|/?TH|/?TR'
1498              .   '|OL|UL|/?LI'
1499              .   '|DL|/?DT|/?DD'
1500              .   '|EMBED|OBJECT|APPLET|/?PARAM'
1501              //. '|SELECT|/?OPTION'
1502              //. '|FIELDSET|/?LEGEND'
1503              //. '|FRAMESET|/?FRAME'
1504              .   ')'
1505              ;
1506  
1507              $space = '(\s|(<br[^>]*>))+';
1508              $search = '#(<'.$htmltags.'[^>]*'.'>)'.$space.'(?='.'<)#is';
1509              $value = preg_replace($search, '\\1', $value);
1510  
1511              // replace remaining newlines with <br />
1512              $value = str_replace("\n", '<br />', $value);
1513  
1514              // encode unicode characters as HTML entities
1515              // (in particular, accented charaters that have not been encoded by HP)
1516  
1517              // unicode characters can be detected by checking the hex value of a character
1518              //  00 - 7F : ascii char (roman alphabet + punctuation)
1519              //  80 - BF : byte 2, 3 or 4 of a unicode char
1520              //  C0 - DF : 1st byte of 2-byte char
1521              //  E0 - EF : 1st byte of 3-byte char
1522              //  F0 - FF : 1st byte of 4-byte char
1523              // if the string doesn't match the above, it might be
1524              //  80 - FF : single-byte, non-ascii char
1525              $search = '#('.'[\xc0-\xdf][\x80-\xbf]'.'|'.'[\xe0-\xef][\x80-\xbf]{2}'.'|'.'[\xf0-\xff][\x80-\xbf]{3}'.'|'.'[\x80-\xff]'.')#se';
1526              $value = preg_replace($search, "hotpot_utf8_to_html_entity('\\1')", $value);
1527          }
1528          return $value;
1529      }
1530      function xml_values($tags) {
1531          $i = 0;
1532          $values = array();
1533          while ($value = $this->xml_value($tags, "[$i]['#']")) {
1534              $values[$i++] = $value;
1535          }
1536          return $values;
1537      }
1538      function obj_value(&$obj, $name) {
1539          return is_object($obj) ? @$obj->$name : (is_array($obj) ? @$obj[$name] : NULL);
1540      }
1541      function encode_cdata(&$str, $tag) {
1542  
1543          // conversion tables
1544          static $HTML_ENTITIES = array(
1545              '&apos;' => "'",
1546              '&quot;' => '"',
1547              '&lt;'   => '<',
1548              '&gt;'   => '>',
1549              '&amp;'  => '&',
1550          );
1551          static $ILLEGAL_STRINGS = array(
1552              "\r\n"  => '&lt;br /&gt;',
1553              "\r"    => '&lt;br /&gt;',
1554              "\n"    => '&lt;br /&gt;',
1555              '['     => '&#91;',
1556              ']'     => '&#93;'
1557          );
1558  
1559          // extract the $tag from the $str(ing), if possible
1560          $pattern = '|(^.*<'.$tag.'[^>]*)(>.*<)(/'.$tag.'>.*$)|is';
1561          if (preg_match($pattern, $str, $matches)) {
1562  
1563              // encode problematic CDATA chars and strings
1564              $matches[2] = strtr($matches[2], $ILLEGAL_STRINGS);
1565  
1566              // if there are any ampersands in "open text"
1567              // surround them by CDATA start and end markers
1568              // (and convert HTML entities to plain text)
1569              $search = '/>([^<]*&[^<]*)</e';
1570              $replace = '"><![CDATA[".strtr("$1", $HTML_ENTITIES)."]]><"';
1571              $matches[2] = preg_replace($search, $replace, $matches[2]);
1572  
1573              $str = $matches[1].$matches[2].$matches[3];
1574          }
1575      }
1576  }
1577  
1578  class hotpot_xml_quiz extends hotpot_xml_tree {
1579  
1580      // constructor function
1581      function hotpot_xml_quiz(&$obj, $read_file=true, $parse_xml=true, $convert_urls=true, $report_errors=true, $create_html=true) {
1582          // obj can be the $_GET array or a form object/array
1583  
1584          global $CFG, $HOTPOT_OUTPUTFORMAT, $HOTPOT_OUTPUTFORMAT_DIR;
1585  
1586          // check xmlize functions are available
1587          if (! function_exists("xmlize")) {
1588              error('xmlize functions are not available');
1589          }
1590  
1591          $this->read_file = $read_file;
1592          $this->parse_xml = $parse_xml;
1593          $this->convert_urls = $convert_urls;
1594          $this->report_errors = $report_errors;
1595          $this->create_html = $create_html;
1596  
1597          // extract fields from $obj
1598          //  course     : the course id
1599          //  reference   : the filename within the files folder
1600          //  location     : "site" files folder or "course" files folder
1601          //  navigation   : type of navigation required in quiz
1602          //  forceplugins : force Moodle compatible media players
1603          $this->course = $this->obj_value($obj, 'course');
1604          $this->reference = $this->obj_value($obj, 'reference');
1605          $this->location = $this->obj_value($obj, 'location');
1606          $this->navigation = $this->obj_value($obj, 'navigation');
1607          $this->forceplugins = $this->obj_value($obj, 'forceplugins');
1608  
1609          // can't continue if there is no course or reference
1610          if (empty($this->course) || empty($this->reference)) {
1611              $this->error = get_string('error_nocourseorfilename', 'hotpot');
1612              if ($this->report_errors) {
1613                  error($this->error);
1614              }
1615              return;
1616          }
1617  
1618          $this->course_homeurl = "$CFG->wwwroot/course/view.php?id=$this->course";
1619  
1620          // set filedir, filename and filepath
1621          switch ($this->location) {
1622              case HOTPOT_LOCATION_SITEFILES:
1623                  $site = get_site();
1624                  $this->filedir = $site->id;
1625                  break;
1626  
1627              case HOTPOT_LOCATION_COURSEFILES:
1628              default:
1629                  $this->filedir = $this->course;
1630                  break;
1631          }
1632          $this->filesubdir = dirname($this->reference);
1633          if ($this->filesubdir=='.') {
1634              $this->filesubdir = '';
1635          }
1636          if ($this->filesubdir) {
1637              $this->filesubdir .= '/';
1638          }
1639          $this->filename = basename($this->reference);
1640          $this->fileroot = "$CFG->dataroot/$this->filedir";
1641          $this->filepath = "$this->fileroot/$this->reference";
1642  
1643          // read the file, if required
1644          if ($this->read_file) {
1645  
1646              if (!file_exists($this->filepath) || !is_readable($this->filepath)) {
1647                  $this->error = get_string('error_couldnotopensourcefile', 'hotpot', $this->filepath);
1648                  if ($this->report_errors) {
1649                      error($this->error, $this->course_homeurl);
1650                  }
1651                  return;
1652              }
1653  
1654              // read in the XML source
1655              $this->source = file_get_contents($this->filepath);
1656  
1657              // convert relative URLs to absolute URLs
1658              if ($this->convert_urls) {
1659                  $this->hotpot_convert_relative_urls($this->source);
1660              }
1661  
1662              $this->html = '';
1663              $this->quiztype = '';
1664              $this->outputformat = 0;
1665  
1666              // is this an html file?
1667              if (preg_match('|\.html?$|', $this->filename)) {
1668  
1669                  $this->filetype = 'html';
1670                  $this->html = &$this->source;
1671  
1672                  // relative URLs in stylesheets
1673                  $search = '|'.'(<style[^>]*>)'.'(.*?)'.'(</style>)'.'|ise';
1674                  $replace = "hotpot_stripslashes('\\1').hotpot_convert_stylesheets_urls('".$this->get_baseurl()."','".$this->reference."','\\2'.'\\3')";
1675                  $this->source = preg_replace($search, $replace, $this->source);
1676  
1677                  // relative URLs in "PreloadImages(...);"
1678                  $search = '|'.'(?<='.'PreloadImages'.'\('.')'."([^)]+?)".'(?='.'\);'.')'.'|se';
1679                  $replace = "hotpot_convert_preloadimages_urls('".$this->get_baseurl()."','".$this->reference."','\\1')";
1680                  $this->source = preg_replace($search, $replace, $this->source);
1681  
1682                  // relative URLs in <button class="NavButton" ... onclick="location='...'">
1683                  $search = '|'.'(?<='.'onclick="'."location='".')'."([^']*)".'(?='."'; return false;".'")'.'|ise';
1684                  $replace = "hotpot_convert_navbutton_url('".$this->get_baseurl()."','".$this->reference."','\\1','".$this->course."')";
1685                  $this->source = preg_replace($search, $replace, $this->source);
1686  
1687                  // relative URLs in <a ... onclick="window.open('...')...">...</a>
1688                  $search = '|'.'(?<='.'onclick="'."window.open\\('".')'."([^']*)".'(?='."'\\);return false;".'")'.'|ise';
1689                  $replace = "hotpot_convert_url('".$this->get_baseurl()."','".$this->reference."','\\1')";
1690                  $this->source = preg_replace($search, $replace, $this->source);
1691  
1692              } else {
1693  
1694                  // relative URLs in <a ... onclick="window.open('...')...">...</a>
1695                  $search = '|'.'(?<='.'onclick=&quot;'."window.open\\(&apos;".')'."(.*?)".'(?='."&apos;\\);return false;".'&quot;)'.'|ise';
1696                  $replace = "hotpot_convert_url('".$this->get_baseurl()."','".$this->reference."','\\1')";
1697                  $this->source = preg_replace($search, $replace, $this->source);
1698  
1699                  if ($this->parse_xml) {
1700  
1701                      $this->filetype = 'xml';
1702  
1703                      // encode "gap fill" text in JCloze exercise
1704                      $this->encode_cdata($this->source, 'gap-fill');
1705  
1706                      // convert source to xml tree
1707                      $this->hotpot_xml_tree($this->source);
1708  
1709                      $keys = array_keys($this->xml);
1710                      foreach ($keys as $key) {
1711                          if (preg_match('/^(hotpot|textoys)-(\w+)-file$/i', $key, $matches)) {
1712                              $this->quiztype = strtolower($matches[2]);
1713                              $this->xml_root = "['$key']['#']";
1714                              break;
1715                          }
1716                      }
1717                  }
1718  
1719                  if ($this->create_html) {
1720  
1721                      // set the real output format from the requested output format
1722                      $this->real_outputformat = $this->obj_value($obj, 'outputformat');
1723                      $this->draganddrop = '';
1724                      if (
1725                          empty($this->real_outputformat) ||
1726                          $this->real_outputformat==HOTPOT_OUTPUTFORMAT_BEST ||
1727                          empty($HOTPOT_OUTPUTFORMAT_DIR[$this->real_outputformat])
1728                      ) {
1729                          if ($CFG->hotpotismobile && isset($HOTPOT_OUTPUTFORMAT_DIR[HOTPOT_OUTPUTFORMAT_MOBILE])) {
1730                                  $this->real_outputformat = HOTPOT_OUTPUTFORMAT_MOBILE;
1731                          } else { // PC
1732                              if ($this->quiztype=='jmatch' || $this->quiztype=='jmix') {
1733                                  $this->real_outputformat = HOTPOT_OUTPUTFORMAT_V6_PLUS;
1734                              } else {
1735