]> git.mjollnir.org Git - moodle.git/commitdiff
corrected handling of tables within JCloze text (a lot of vertical space was being...
authorgbateson <gbateson>
Tue, 18 Apr 2006 12:20:18 +0000 (12:20 +0000)
committergbateson <gbateson>
Tue, 18 Apr 2006 12:20:18 +0000 (12:20 +0000)
mod/hotpot/lib.php

index dd4c13840cdf3996e82771543203dba7db72df85..bb6b0d8db4a6c65f29773a2bae60fe9b4c60b074 100644 (file)
-<?PHP  // $Id$\r
-\r
-//////////////////////////////////\r
-/// CONFIGURATION settings\r
-\r
-if (!isset($CFG->hotpot_showtimes)) {\r
-       set_config("hotpot_showtimes", 0);\r
-}\r
-if (!isset($CFG->hotpot_excelencodings)) {\r
-       set_config("hotpot_excelencodings", "");\r
-}\r
-\r
-//////////////////////////////////\r
-/// CONSTANTS and GLOBAL VARIABLES\r
-\r
-$CFG->hotpotroot = "$CFG->dirroot/mod/hotpot";\r
-$CFG->hotpottemplate = "$CFG->hotpotroot/template";\r
-$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']);\r
-\r
-define("HOTPOT_JS", "$CFG->wwwroot/mod/hotpot/hotpot-full.js");\r
-\r
-define("HOTPOT_NO",  "0");\r
-define("HOTPOT_YES", "1");\r
-\r
-define ("HOTPOT_TEXTSOURCE_QUIZ", "0");\r
-define ("HOTPOT_TEXTSOURCE_FILENAME", "1");\r
-define ("HOTPOT_TEXTSOURCE_FILEPATH", "2");\r
-define ("HOTPOT_TEXTSOURCE_SPECIFIC", "3");\r
-\r
-define("HOTPOT_LOCATION_COURSEFILES", "0");\r
-define("HOTPOT_LOCATION_SITEFILES",   "1");\r
-\r
-$HOTPOT_LOCATION = array (\r
-       HOTPOT_LOCATION_COURSEFILES => get_string("coursefiles"),\r
-       HOTPOT_LOCATION_SITEFILES   => get_string("sitefiles"),\r
-);\r
-\r
-define("HOTPOT_OUTPUTFORMAT_BEST",     "1");\r
-define("HOTPOT_OUTPUTFORMAT_V3",      "10");\r
-define("HOTPOT_OUTPUTFORMAT_V4",      "11");\r
-define("HOTPOT_OUTPUTFORMAT_V5",      "12");\r
-define("HOTPOT_OUTPUTFORMAT_V5_PLUS", "13");\r
-define("HOTPOT_OUTPUTFORMAT_V6",      "14");\r
-define("HOTPOT_OUTPUTFORMAT_V6_PLUS", "15");\r
-define("HOTPOT_OUTPUTFORMAT_FLASH",   "20");\r
-define("HOTPOT_OUTPUTFORMAT_MOBILE",  "30");\r
-\r
-$HOTPOT_OUTPUTFORMAT = array (\r
-       HOTPOT_OUTPUTFORMAT_BEST    => get_string("outputformat_best", "hotpot"),\r
-       HOTPOT_OUTPUTFORMAT_V6_PLUS => get_string("outputformat_v6_plus", "hotpot"),\r
-       HOTPOT_OUTPUTFORMAT_V6      => get_string("outputformat_v6", "hotpot"),\r
-       HOTPOT_OUTPUTFORMAT_V5_PLUS => get_string("outputformat_v5_plus", "hotpot"),\r
-       HOTPOT_OUTPUTFORMAT_V5      => get_string("outputformat_v5", "hotpot"),\r
-       HOTPOT_OUTPUTFORMAT_V4      => get_string("outputformat_v4", "hotpot"),\r
-       HOTPOT_OUTPUTFORMAT_V3      => get_string("outputformat_v3", "hotpot"),\r
-       HOTPOT_OUTPUTFORMAT_FLASH   => get_string("outputformat_flash", "hotpot"),\r
-       HOTPOT_OUTPUTFORMAT_MOBILE  => get_string("outputformat_mobile", "hotpot"),\r
-);\r
-$HOTPOT_OUTPUTFORMAT_DIR = array (\r
-       HOTPOT_OUTPUTFORMAT_V6_PLUS => 'v6',\r
-       HOTPOT_OUTPUTFORMAT_V6      => 'v6',\r
-       HOTPOT_OUTPUTFORMAT_V5_PLUS => 'v5',\r
-       HOTPOT_OUTPUTFORMAT_V5      => 'v5',\r
-       HOTPOT_OUTPUTFORMAT_V4      => 'v4',\r
-       HOTPOT_OUTPUTFORMAT_V3      => 'v3',\r
-       HOTPOT_OUTPUTFORMAT_FLASH   => 'flash',\r
-       HOTPOT_OUTPUTFORMAT_MOBILE  => 'mobile',\r
-);\r
-foreach ($HOTPOT_OUTPUTFORMAT_DIR as $format=>$dir) {\r
-       if (is_file("$CFG->hotpottemplate/$dir.php") && is_dir("$CFG->hotpottemplate/$dir")) {\r
-               // do nothing ($format is available)\r
-       } else {\r
-               // $format is not available, so remove it\r
-               unset($HOTPOT_OUTPUTFORMAT[$format]);\r
-               unset($HOTPOT_OUTPUTFORMAT_DIR[$format]);\r
-       }\r
-}\r
-define("HOTPOT_NAVIGATION_BAR",     "1");\r
-define("HOTPOT_NAVIGATION_FRAME",   "2");\r
-define("HOTPOT_NAVIGATION_IFRAME",  "3");\r
-define("HOTPOT_NAVIGATION_BUTTONS", "4");\r
-define("HOTPOT_NAVIGATION_GIVEUP",  "5");\r
-define("HOTPOT_NAVIGATION_NONE",    "6");\r
-\r
-$HOTPOT_NAVIGATION = array (\r
-       HOTPOT_NAVIGATION_BAR     => get_string("navigation_bar", "hotpot"),\r
-       HOTPOT_NAVIGATION_FRAME   => get_string("navigation_frame", "hotpot"),\r
-       HOTPOT_NAVIGATION_IFRAME  => get_string("navigation_iframe", "hotpot"),\r
-       HOTPOT_NAVIGATION_BUTTONS => get_string("navigation_buttons", "hotpot"),\r
-       HOTPOT_NAVIGATION_GIVEUP  => get_string("navigation_give_up", "hotpot"),\r
-       HOTPOT_NAVIGATION_NONE    => get_string("navigation_none", "hotpot"),\r
-);\r
-\r
-define("HOTPOT_JCB",    "1");\r
-define("HOTPOT_JCLOZE", "2");\r
-define("HOTPOT_JCROSS", "3");\r
-define("HOTPOT_JMATCH", "4");\r
-define("HOTPOT_JMIX",   "5");\r
-define("HOTPOT_JQUIZ",  "6");\r
-define("HOTPOT_TEXTOYS_RHUBARB",   "7");\r
-define("HOTPOT_TEXTOYS_SEQUITUR",  "8");\r
-\r
-$HOTPOT_QUIZTYPE = array(\r
-       HOTPOT_JCB    => 'JCB',\r
-       HOTPOT_JCLOZE => 'JCloze',\r
-       HOTPOT_JCROSS => 'JCross',\r
-       HOTPOT_JMATCH => 'JMatch',\r
-       HOTPOT_JMIX   => 'JMix',\r
-       HOTPOT_JQUIZ  => 'JQuiz',\r
-       HOTPOT_TEXTOYS_RHUBARB  => 'Rhubarb',\r
-       HOTPOT_TEXTOYS_SEQUITUR => 'Sequitur'\r
-);\r
-\r
-define("HOTPOT_JQUIZ_MULTICHOICE", "1");\r
-define("HOTPOT_JQUIZ_SHORTANSWER", "2");\r
-define("HOTPOT_JQUIZ_HYBRID",      "3");\r
-define("HOTPOT_JQUIZ_MULTISELECT", "4");\r
-\r
-define("HOTPOT_GRADEMETHOD_HIGHEST", "1");\r
-define("HOTPOT_GRADEMETHOD_AVERAGE", "2");\r
-define("HOTPOT_GRADEMETHOD_FIRST",   "3");\r
-define("HOTPOT_GRADEMETHOD_LAST",    "4");\r
-\r
-$HOTPOT_GRADEMETHOD = array (\r
-       HOTPOT_GRADEMETHOD_HIGHEST => get_string("gradehighest", "quiz"),\r
-       HOTPOT_GRADEMETHOD_AVERAGE => get_string("gradeaverage", "quiz"),\r
-       HOTPOT_GRADEMETHOD_FIRST   => get_string("attemptfirst", "quiz"),\r
-       HOTPOT_GRADEMETHOD_LAST    => get_string("attemptlast",  "quiz"),\r
-);\r
-\r
-define("HOTPOT_STATUS_INPROGRESS", "1");\r
-define("HOTPOT_STATUS_TIMEDOUT",   "2");\r
-define("HOTPOT_STATUS_ABANDONED",  "3");\r
-define("HOTPOT_STATUS_COMPLETED",  "4");\r
-\r
-$HOTPOT_STATUS = array (\r
-       HOTPOT_STATUS_INPROGRESS => get_string("inprogress", "hotpot"),\r
-       HOTPOT_STATUS_TIMEDOUT   => get_string("timedout",   "hotpot"),\r
-       HOTPOT_STATUS_ABANDONED  => get_string("abandoned",  "hotpot"),\r
-       HOTPOT_STATUS_COMPLETED  => get_string("completed",  "hotpot"),\r
-);\r
-\r
-define("HOTPOT_FEEDBACK_NONE", "0");\r
-define("HOTPOT_FEEDBACK_WEBPAGE", "1");\r
-define("HOTPOT_FEEDBACK_FORMMAIL", "2");\r
-define("HOTPOT_FEEDBACK_MOODLEFORUM", "3");\r
-define("HOTPOT_FEEDBACK_MOODLEMESSAGING", "4");\r
-\r
-$HOTPOT_FEEDBACK = array (\r
-       HOTPOT_FEEDBACK_NONE => get_string("feedbacknone", "hotpot"),\r
-       HOTPOT_FEEDBACK_WEBPAGE => get_string("feedbackwebpage",  "hotpot"),\r
-       HOTPOT_FEEDBACK_FORMMAIL => get_string("feedbackformmail", "hotpot"),\r
-       HOTPOT_FEEDBACK_MOODLEFORUM => get_string("feedbackmoodleforum", "hotpot"),\r
-       HOTPOT_FEEDBACK_MOODLEMESSAGING => get_string("feedbackmoodlemessaging", "hotpot"),\r
-);\r
-if (empty($CFG->messaging)) { // Moodle 1.4 (and less)\r
-       unset($HOTPOT_FEEDBACK[HOTPOT_FEEDBACK_MOODLEMESSAGING]);\r
-}\r
-\r
-define("HOTPOT_DISPLAYNEXT_QUIZ",   "0");\r
-define("HOTPOT_DISPLAYNEXT_COURSE", "1");\r
-define("HOTPOT_DISPLAYNEXT_INDEX",  "2");\r
-\r
-//////////////////////////////////\r
-/// CORE FUNCTIONS\r
-\r
-\r
-// possible return values:\r
-//    false:\r
-//        display moderr.html (if exists) OR "Could not update" and return to couse view\r
-//    string:\r
-//        display as error message and return to course view\r
-//  true (or non-zero number):\r
-//        continue to $hp->redirect (if set) OR hotpot/view.php (to displsay quiz)\r
-\r
-// $hp is an object containing the values of the form in mod.html\r
-// i.e. all the fields in the 'hotpot' table, plus the following:\r
-//     $hp->course       : an id in the 'course' table\r
-//     $hp->coursemodule : an id in the 'course_modules' table\r
-//     $hp->section      : an id in the 'course_sections' table\r
-//     $hp->module       : an id in the 'modules' table\r
-//     $hp->modulename   : always 'hotpot'\r
-//     $hp->instance     : an id in the 'hotpot' table\r
-//     $hp->mode         : 'add' or 'update'\r
-//     $hp->sesskey      : unique string required for Moodle's session management\r
-\r
-function hotpot_add_instance(&$hp) {\r
-       if (hotpot_set_form_values($hp)) {\r
-               $result = insert_record("hotpot", $hp);\r
-       } else {\r
-               $result=  false;\r
-       }\r
-       return $result;\r
-}\r
-\r
-function hotpot_update_instance(&$hp) {\r
-       if (hotpot_set_form_values($hp)) {\r
-               $hp->id = $hp->instance;\r
-               $result = update_record("hotpot", $hp);\r
-       } else {\r
-               $result=  false;\r
-       }\r
-       return $result;\r
-}\r
-\r
-function hotpot_set_form_values(&$hp) {\r
-       $ok = true;\r
-       $hp->errors = array(); // these will be reported by moderr.html\r
-\r
-       if (empty($hp->reference)) {\r
-               $ok = false;\r
-               $hp->errors['reference']= get_string('error_nofilename', 'hotpot');\r
-       }\r
-\r
-       if ($hp->studentfeedbackurl=='http://') {\r
-               $hp->studentfeedbackurl = '';\r
-       }\r
-\r
-       if (empty($hp->studentfeedbackurl)) {\r
-               switch ($hp->studentfeedback) {\r
-                       case HOTPOT_FEEDBACK_WEBPAGE:\r
-                               $ok = false;\r
-                               $hp->errors['studentfeedbackurl']= get_string('error_nofeedbackurlwebpage', 'hotpot');\r
-                       break;\r
-                       case HOTPOT_FEEDBACK_FORMMAIL:\r
-                               $ok = false;\r
-                               $hp->errors['studentfeedbackurl']= get_string('error_nofeedbackurlformmail', 'hotpot');\r
-                       break;\r
-               }\r
-       }\r
-\r
-       $time = time();\r
-       $hp->timecreated = $time;\r
-       $hp->timemodified = $time;\r
-\r
-       if (empty($hp->enabletimeopen)) {\r
-               $hp->timeopen = 0;\r
-       } else {\r
-               $hp->timeopen = make_timestamp(\r
-                       $hp->openyear, $hp->openmonth, $hp->openday,\r
-                       $hp->openhour, $hp->openminute, 0\r
-               );\r
-       }\r
-\r
-       if (empty($hp->enabletimeclose)) {\r
-               $hp->timeclose = 0;\r
-       } else {\r
-               $hp->timeclose = make_timestamp(\r
-                       $hp->closeyear, $hp->closemonth, $hp->closeday,\r
-                       $hp->closehour, $hp->closeminute, 0\r
-               );\r
-       }\r
-\r
-       if ($hp->quizchain==HOTPOT_YES) {\r
-               switch ($hp->mode) {\r
-                       case 'add':\r
-                               $ok = hotpot_add_chain($hp);\r
-                       break;\r
-                       case 'update':\r
-                               $ok = hotpot_update_chain($hp);\r
-                       break;\r
-               }\r
-       } else { // $hp->quizchain==HOTPOT_NO\r
-               hotpot_set_name_summary_reference($hp);\r
-       }\r
-\r
-       switch ($hp->displaynext) {\r
-               // N.B. redirection only works for Moodle 1.5+\r
-               case HOTPOT_DISPLAYNEXT_COURSE:\r
-                       $hp->redirect = true;\r
-                       $hp->redirecturl = "view.php?id=$hp->course";\r
-                       break;\r
-               case HOTPOT_DISPLAYNEXT_INDEX:\r
-                       $hp->redirect = true;\r
-                       $hp->redirecturl = "../mod/hotpot/index.php?id=$hp->course";\r
-                       break;\r
-               default:\r
-                       // use Moodle default action (i.e. go on to display the hotpot quiz)\r
-       }\r
-\r
-       // if ($ok && $hp->setdefaults) {\r
-       if ($ok) {\r
-               set_user_preference('hotpot_timeopen', $hp->timeopen);\r
-               set_user_preference('hotpot_timeclose', $hp->timeclose);\r
-               set_user_preference('hotpot_navigation', $hp->navigation);\r
-               set_user_preference('hotpot_outputformat', $hp->outputformat);\r
-               set_user_preference('hotpot_studentfeedback', $hp->studentfeedback);\r
-               set_user_preference('hotpot_studentfeedbackurl', $hp->studentfeedbackurl);\r
-               set_user_preference('hotpot_forceplugins', $hp->forceplugins);\r
-               set_user_preference('hotpot_shownextquiz', $hp->shownextquiz);\r
-               set_user_preference('hotpot_review', $hp->review);\r
-               set_user_preference('hotpot_grade', $hp->grade);\r
-               set_user_preference('hotpot_grademethod', $hp->grademethod);\r
-               set_user_preference('hotpot_attempts', $hp->attempts);\r
-               set_user_preference('hotpot_subnet', $hp->subnet);\r
-               set_user_preference('hotpot_displaynext', $hp->displaynext);\r
-               if ($hp->mode=='add') {\r
-                       set_user_preference('hotpot_quizchain', $hp->quizchain);\r
-                       set_user_preference('hotpot_namesource', $hp->namesource);\r
-                       set_user_preference('hotpot_summarysource', $hp->summarysource);\r
-               }\r
-       }\r
-       return $ok;\r
-}\r
-function hotpot_get_chain(&$cm) {\r
-       // get details of course_modules in this section\r
-       $course_module_ids = get_field('course_sections', 'sequence', 'id', $cm->section);\r
-       if (empty($course_module_ids)) {\r
-               $hotpot_modules = array();\r
-       } else {\r
-               $hotpot_modules = get_records_select('course_modules', "id IN ($course_module_ids) AND module=$cm->module");\r
-               if (empty($hotpot_modules)) {\r
-                       $hotpot_modules = array();\r
-               }\r
-       }\r
-\r
-       // get ids of hotpot modules in this section\r
-       $ids = array();\r
-       foreach ($hotpot_modules as $hotpot_module) {\r
-               $ids[] = $hotpot_module->instance;\r
-       }\r
-\r
-       // get details of hotpots in this section\r
-       if (empty($ids)) {\r
-               $hotpots = array();\r
-       } else {\r
-               $hotpots = get_records_list('hotpot', 'id', implode(',', $ids));\r
-       }\r
-\r
-       $found = false;\r
-       $chain = array();\r
-\r
-       // loop through course_modules in this section\r
-       $ids = explode(',', $course_module_ids);\r
-       foreach ($ids as $id) {\r
-\r
-               // check this course_module is a hotpot activity\r
-               if (isset($hotpot_modules[$id])) {\r
-\r
-                       // store details of this course module and hotpot activity\r
-                       $hotpot_id = $hotpot_modules[$id]->instance;\r
-                       $chain[$id] = &$hotpot_modules[$id];\r
-                       $chain[$id]->hotpot = &$hotpots[$hotpot_id];\r
-\r
-                       // set $found, if this is the course module we're looking for\r
-                       if (isset($cm->coursemodule)) {\r
-                               if ($id==$cm->coursemodule) {\r
-                                       $found = true;\r
-                               }\r
-                       } else {\r
-                               if ($id==$cm->id) {\r
-                                       $found = true;\r
-                               }\r
-                       }\r
-\r
-                       // is this the end of a chain\r
-                       if (empty($hotpots[$hotpot_id]->shownextquiz)) {\r
-                               if ($found) {\r
-                                       break; // out of loop\r
-                               } else {\r
-                                       // restart chain (target cm has not been found yet)\r
-                                       $chain = array();\r
-                               }\r
-                       }\r
-               }\r
-       } // end foreach $ids\r
-\r
-       return $found ? $chain : false;\r
-}\r
-function hotpot_is_visible(&$cm) {\r
-       if (!isset($cm->sectionvisible)) {\r
-               if ($section = get_record('course_sections', 'id', $cm->section)) {\r
-                       $cm->sectionvisible = $section->visible;\r
-               } else {\r
-                       error('Course module record contains invalid section');\r
-               }\r
-       }\r
-\r
-       if (empty($cm->sectionvisible)) {\r
-               $visible = HOTPOT_NO;\r
-       } else {\r
-               $visible = HOTPOT_YES;\r
-               if (empty($cm->visible)) {\r
-                       if ($chain = hotpot_get_chain($cm)) {\r
-                               $startofchain = array_shift($chain);\r
-                               $visible = $startofchain->visible;\r
-                       }\r
-               }\r
-       }\r
-       return $visible;\r
-}\r
-function hotpot_add_chain(&$hp) {\r
-/// add a chain of hotpot actiivities\r
-\r
-       global $CFG, $course;\r
-\r
-       $ok = true;\r
-       $hp->names = array();\r
-       $hp->summaries = array();\r
-       $hp->references = array();\r
-\r
-       $xml_quiz = new hotpot_xml_quiz($hp, false, false, false, false, false);\r
-\r
-       if (isset($xml_quiz->error)) {\r
-               $hp->errors['reference'] = $xml_quiz->error;\r
-               $ok = false;\r
-\r
-       } else if (is_dir($xml_quiz->filepath)) {\r
-\r
-               // get list of hotpot files in this folder\r
-               if ($dh = @opendir($xml_quiz->filepath)) {\r
-                       while ($file = @readdir($dh)) {\r
-                               if (preg_match('/\.(jbc|jcl|jcw|jmt|jmx|jqz|htm|html)$/', $file)) {\r
-                                       $hp->references[] = "$xml_quiz->reference/$file";\r
-                               }\r
-                       }\r
-                       closedir($dh);\r
-\r
-                       // get titles\r
-                       foreach ($hp->references as $i=>$reference) {\r
-                               $filepath = $xml_quiz->fileroot.'/'.$reference;\r
-                               hotpot_get_titles_and_next_ex($hp, $filepath);\r
-                               $hp->names[$i] = $hp->exercisetitle;\r
-                               $hp->summaries[$i] = $hp->exercisesubtitle;\r
-                       }\r
-\r
-               } else {\r
-                       $ok = false;\r
-                       $hp->errors['reference'] = get_string('error_couldnotopenfolder', 'hotpot', $hp->reference);\r
-               }\r
-\r
-       } else if (is_file($xml_quiz->filepath)) {\r
-\r
-               $filerootlength = strlen($xml_quiz->fileroot) + 1;\r
-\r
-               while ($xml_quiz->filepath) {\r
-                       hotpot_get_titles_and_next_ex($hp, $xml_quiz->filepath, true);\r
-                       $hp->names[] = $hp->exercisetitle;\r
-                       $hp->summaries[] = $hp->exercisesubtitle;\r
-                       $hp->references[] = substr($xml_quiz->filepath, $filerootlength);\r
-\r
-                       if ($hp->nextexercise) {\r
-                               $filepath = $xml_quiz->fileroot.'/'.$xml_quiz->filesubdir.$hp->nextexercise;\r
-\r
-                               // check file is not already in chain\r
-                               $reference = substr($filepath, $filerootlength);\r
-                               if (in_array($reference, $hp->references)) {\r
-                                       $filepath = '';\r
-                               }\r
-                       } else {\r
-                               $filepath = '';\r
-                       }\r
-                       if ($filepath && file_exists($filepath) && is_file($filepath) && is_readable($filepath)) {\r
-                               $xml_quiz->filepath = $filepath;\r
-                       } else {\r
-                               $xml_quiz->filepath = false; // finish while loop\r
-                       }\r
-               } // end while\r
-\r
-       } else {\r
-               $ok = false;\r
-               $hp->errors['reference'] = get_string('error_notfileorfolder', 'hotpot', $hp->reference);\r
-       }\r
-\r
-       if (empty($hp->references) && empty($hp->errors['reference'])) {\r
-               $ok = false;\r
-               $hp->errors['reference'] = get_string('error_noquizzesfound', 'hotpot', $hp->reference);\r
-       }\r
-\r
-       if ($ok) {\r
-               $hp->visible = HOTPOT_YES;\r
-\r
-               if (trim($hp->name)=='') {\r
-                       $hp->name = get_string("modulename", $hp->modulename);\r
-               }\r
-               $hp->specificname = $hp->name;\r
-               $hp->specificsummary = $hp->summary;\r
-\r
-               // add all except last activity in chain\r
-\r
-               $i_max = count($hp->references)-1;\r
-               for ($i=0; $i<$i_max; $i++) {\r
-\r
-                       hotpot_set_name_summary_reference($hp, $i);\r
-                       $hp->reference = addslashes($hp->reference);\r
-\r
-                       if (!$hp->instance = insert_record("hotpot", $hp)) {\r
-                               error("Could not add a new instance of $hp->modulename", "view.php?id=$hp->course");\r
-                       }\r
-\r
-                       // store (hotpot table) id of start of chain\r
-                       if ($i==0) {\r
-                               $hp->startofchain = $hp->instance;\r
-                       }\r
-\r
-                       if (isset($course->groupmode)) {\r
-                               $hp->groupmode = $course->groupmode;\r
-                       }\r
-\r
-                       if (! $hp->coursemodule = add_course_module($hp)) {\r
-                               error("Could not add a new course module");\r
-                       }\r
-                       if (! $sectionid = add_mod_to_section($hp) ) {\r
-                               error("Could not add the new course module to that section");\r
-                       }\r
-\r
-                       if (! set_field("course_modules", "section", $sectionid, "id", $hp->coursemodule)) {\r
-                               error("Could not update the course module with the correct section");\r
-                       }\r
-\r
-                       add_to_log($hp->course, "course", "add mod",\r
-                               "../mod/$hp->modulename/view.php?id=$hp->coursemodule",\r
-                               "$hp->modulename $hp->instance"\r
-                       );\r
-                       add_to_log($hp->course, $hp->modulename, "add",\r
-                               "view.php?id=$hp->coursemodule",\r
-                               "$hp->instance", $hp->coursemodule\r
-                       );\r
-\r
-                       // hide tail of chain\r
-                       if ($hp->shownextquiz==HOTPOT_YES) {\r
-                               $hp->visible = HOTPOT_NO;\r
-                       }\r
-               } // end for ($hp->references)\r
-\r
-               // settings for final activity in chain\r
-               hotpot_set_name_summary_reference($hp, $i);\r
-               $hp->reference = addslashes($hp->references[$i]);\r
-               $hp->shownextquiz = HOTPOT_NO;\r
-\r
-               if (isset($hp->startofchain)) {\r
-                       // redirection only works for Moodle 1.5+\r
-                       $hp->redirect = true;\r
-                       $hp->redirecturl = "$CFG->wwwroot/mod/hotpot/view.php?hp=$hp->startofchain";\r
-               }\r
-       } // end if $ok\r
-\r
-       return $ok;\r
-}\r
-function hotpot_set_name_summary_reference(&$hp, $chain_index=NULL) {\r
-\r
-       $xml_quiz = NULL;\r
-\r
-       $textfields = array('name', 'summary');\r
-       foreach ($textfields as $textfield) {\r
-\r
-               $textsource = $textfield.'source';\r
-\r
-               // are we adding a chain?\r
-               if (isset($chain_index)) {\r
-\r
-                       switch ($hp->$textsource) {\r
-                               case HOTPOT_TEXTSOURCE_QUIZ:\r
-                                       if ($textfield=='name') {\r
-                                               $hp->exercisetitle = $hp->names[$chain_index];\r
-                                       } else if ($textfield=='summary') {\r
-                                               $hp->exercisesubtitle = $hp->summaries[$chain_index];\r
-                                       }\r
-                                       break;\r
-                               case HOTPOT_TEXTSOURCE_SPECIFIC:\r
-                                       $specifictext = 'specific'.$textfield;\r
-                                       if (empty($hp->$specifictext) && trim($hp->$specifictext)=='') {\r
-                                               $hp->$textfield = '';\r
-                                       } else {\r
-                                               $hp->$textfield = $hp->$specifictext.' ('.($chain_index+1).')';\r
-                                       }\r
-                                       break;\r
-                       }\r
-                       $hp->reference = $hp->references[$chain_index];\r
-               }\r
-\r
-               if ($hp->$textsource==HOTPOT_TEXTSOURCE_QUIZ) {\r
-                       if (empty($xml_quiz) && !isset($chain_index)) {\r
-                               $xml_quiz = new hotpot_xml_quiz($hp, false, false, false, false, false);\r
-                               hotpot_get_titles_and_next_ex($hp, $xml_quiz->filepath);\r
-                       }\r
-                       if ($textfield=='name') {\r
-                               $hp->$textfield = addslashes($hp->exercisetitle);\r
-                       } else if ($textfield=='summary') {\r
-                               $hp->$textfield = addslashes($hp->exercisesubtitle);\r
-                       }\r
-               }\r
-               switch ($hp->$textsource) {\r
-                       case HOTPOT_TEXTSOURCE_FILENAME:\r
-                               $hp->$textfield = basename($hp->reference);\r
-                               break;\r
-                       case HOTPOT_TEXTSOURCE_FILEPATH:\r
-                               $hp->$textfield = '';\r
-                               // continue to next lines\r
-                       default:\r
-                               if (empty($hp->$textfield)) {\r
-                                       $hp->$textfield = str_replace('/', ' ', $hp->reference);\r
-                               }\r
-               } // end switch\r
-       } // end foreach\r
-}\r
-function hotpot_get_titles_and_next_ex(&$hp, $filepath, $get_next=false) {\r
-\r
-       $hp->exercisetitle = '';\r
-       $hp->exercisesubtitle = '';\r
-       $hp->nextexercise = '';\r
-\r
-       // read the quiz file source\r
-       if ($source = file_get_contents($filepath)) {\r
-\r
-               $next = '';\r
-               $title = '';\r
-               $subtitle = '';\r
-\r
-               if (preg_match('|\.html?$|', $filepath)) {\r
-                       // html file\r
-                       if (preg_match('|<h2[^>]*class="ExerciseTitle"[^>]*>(.*?)</h2>|is', $source, $matches)) {\r
-                               $title = trim(strip_tags($matches[1]));\r
-                       }\r
-                       if (empty($title)) {\r
-                               if (preg_match('|<title[^>]*>(.*?)</title>|is', $source, $matches)) {\r
-                                       $title = trim(strip_tags($matches[1]));\r
-                               }\r
-                       }\r
-                       if (preg_match('|<h3[^>]*class="ExerciseSubtitle"[^>]*>(.*?)</h3>|is', $source, $matches)) {\r
-                               $subtitle = trim(strip_tags($matches[1]));\r
-                       }\r
-                       if ($get_next) {\r
-                               if (preg_match('|<div[^>]*class="NavButtonBar"[^>]*>(.*?)</div>|is', $source, $matches)) {\r
-                                       $navbuttonbar = $matches[1];\r
-                                       if (preg_match_all('|<button[^>]*class="NavButton"[^>]*onclick="'."location='([^']*)'".'[^"]*"[^>]*>|is', $navbuttonbar, $matches)) {\r
-                                               $lastbutton = count($matches[0])-1;\r
-                                               $next = $matches[1][$lastbutton];\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-               } else {\r
-                       // xml file (...maybe)\r
-                       $xml_tree = new hotpot_xml_tree($source);\r
-                       $xml_tree->filetype = '';\r
-\r
-                       $keys = array_keys($xml_tree->xml);\r
-                       foreach ($keys as $key) {\r
-                               if (preg_match('/^(hotpot|textoys)-(\w+)-file$/i', $key, $matches)) {\r
-                                       $xml_tree->filetype = 'xml';\r
-                                       $xml_tree->xml_root = "['$key']['#']";\r
-                                       $xml_tree->quiztype = strtolower($matches[2]);\r
-                                       break;\r
-                               }\r
-                       }\r
-                       if ($xml_tree->filetype=='xml') {\r
-\r
-                               $title = strip_tags($xml_tree->xml_value('data,title'));\r
-                               $subtitle = $xml_tree->xml_value('hotpot-config-file,'.$xml_tree->quiztype.',exercise-subtitle');\r
-\r
-                               if ($get_next) {\r
-                                       $include = $xml_tree->xml_value('hotpot-config-file,global,include-next-ex');\r
-                                       if (!empty($include)) {\r
-                                               $next = $xml_tree->xml_value("hotpot-config-file,$xml_tree->quiztype,next-ex-url");\r
-                                               if (is_array($next)) {\r
-                                                       $next = $next[0]; // in case "next-ex-url" was repeated in the xml file\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-\r
-               $hp->nextexercise = $next;\r
-               $hp->exercisetitle = (empty($title) || is_array($title)) ? basename($filepath) : $title;\r
-               $hp->exercisesubtitle = (empty($subtitle) || is_array($subtitle)) ? $hp->exercisetitle : $subtitle;\r
-       }\r
-}\r
-function hotpot_get_all_instances_in_course($modulename, $course) {\r
-/// called from index.php\r
-\r
-       global $CFG;\r
-       $instances = array();\r
-\r
-       if (isset($CFG->release) && substr($CFG->release, 0, 3)>=1.2) {\r
-               $groupmode = 'cm.groupmode,';\r
-       } else {\r
-               $groupmode = '';\r
-       }\r
-\r
-       $query = "\r
-               SELECT\r
-                       cm.id AS coursemodule,\r
-                       cm.course AS course,\r
-                       cm.module AS module,\r
-                       cm.instance AS instance,\r
-                       -- cm.section AS section,\r
-                       cm.visible AS visible,\r
-                       $groupmode\r
-                       -- cs.section AS sectionnumber,\r
-                       cs.section AS section,\r
-                       cs.sequence AS sequence,\r
-                       cs.visible AS sectionvisible,\r
-                       thismodule.*\r
-               FROM\r
-                       {$CFG->prefix}course_modules AS cm,\r
-                       {$CFG->prefix}course_sections AS cs,\r
-                       {$CFG->prefix}modules AS m,\r
-                       {$CFG->prefix}$modulename AS thismodule\r
-               WHERE\r
-                       m.name = '$modulename' AND\r
-                       m.id = cm.module AND\r
-                       cm.course = '$course->id' AND\r
-                       cm.section = cs.id AND\r
-                       cm.instance = thismodule.id\r
-       ";\r
-       if ($rawmods = get_records_sql($query)) {\r
-\r
-               // cache $isteacher setting\r
-               $isteacher = isteacher($course->id);\r
-\r
-               $explodesection = array();\r
-               $order = array();\r
-\r
-               foreach ($rawmods as $rawmod) {\r
-\r
-                       if (empty($explodesection[$rawmod->section])) {\r
-                               $explodesection[$rawmod->section] = true;\r
-\r
-                               $coursemodules = explode(',', $rawmod->sequence);\r
-                               foreach ($coursemodules as $i=>$coursemodule) {\r
-                                       $order[$coursemodule] = sprintf('%d.%04d', $rawmod->section, $i);\r
-                               }\r
-                       }\r
-\r
-                       if ($isteacher) {\r
-                               $visible = true;\r
-                       } else if ($modulename=='hotpot') {\r
-                               $visible = hotpot_is_visible($rawmod);\r
-                       } else {\r
-                               $visible = $rawmod->visible;\r
-                       }\r
-\r
-                       if ($visible) {\r
-                               $instances[$order[$rawmod->coursemodule]] = $rawmod;\r
-                       }\r
-\r
-               } // end foreach $modinfo\r
-\r
-               ksort($instances);\r
-               $instances = array_values($instances);\r
-       }\r
-\r
-       return $instances;\r
-}\r
-\r
-function hotpot_update_chain(&$hp) {\r
-/// update a chain of hotpot actiivities\r
-\r
-       $ok = true;\r
-       if ($hotpot_modules = hotpot_get_chain($hp)) {\r
-\r
-               // skip updating of these fields\r
-               $skipfields = array('id', 'course', 'name', 'reference', 'summary', 'shownextquiz');\r
-               $fields = array();\r
-\r
-               foreach ($hotpot_modules as $hotpot_module) {\r
-\r
-                       if ($hp->instance==$hotpot_module->id) {\r
-                               // don't need to update this hotpot\r
-\r
-                       } else {\r
-                               // shortcut to hotpot record\r
-                               $hotpot = &$hotpot_module->hotpot;\r
-\r
-                               // get a list of fields to update (first time only)\r
-                               if (empty($fields)) {\r
-                                       $fields = array_keys(get_object_vars($hotpot));\r
-                               }\r
-\r
-                               // assume update is NOT required\r
-                               $require_update = false;\r
-\r
-                               // update field values (except $skipfields)\r
-                               foreach($fields as $field) {\r
-                                       if (in_array($field, $skipfields) || $hotpot->$field==$hp->$field) {\r
-                                               // update not required for this field\r
-                                       } else {\r
-                                               $require_update = true;\r
-                                               $hotpot->$field = $hp->$field;\r
-                                       }\r
-                               }\r
-\r
-                               // update this $hotpot, if required\r
-                               if ($require_update && !update_record("hotpot", $hotpot)) {\r
-                                       error("Could not update the $hp->modulename", "view.php?id=$hp->course");\r
-                               }\r
-                       }\r
-               } // end foreach $ids\r
-       }\r
-       return $ok;\r
-}\r
-function hotpot_delete_instance($id) {\r
-/// Given an ID of an instance of this module,\r
-/// this function will permanently delete the instance\r
-/// and any data that depends on it.\r
-\r
-       $result = false;\r
-       if (delete_records("hotpot", "id", "$id")) {\r
-               $result = true;\r
-               delete_records("hotpot_questions", "hotpot", "$id");\r
-               if ($attempts = get_records_select("hotpot_attempts", "hotpot='$id'")) {\r
-                       $ids = implode(',', array_keys($attempts));\r
-                       delete_records_select("hotpot_attempts",  "id IN ($ids)");\r
-                       delete_records_select("hotpot_details",   "attempt IN ($ids)");\r
-                       delete_records_select("hotpot_responses", "attempt IN ($ids)");\r
-               }\r
-       }\r
-       return $result;\r
-}\r
-function hotpot_delete_and_notify($table, $select, $strtable) {\r
-       $count = max(0, count_records_select($table, $select));\r
-       if ($count) {\r
-               delete_records_select($table, $select);\r
-               $count -= max(0, count_records_select($table, $select));\r
-               if ($count) {\r
-                       notify(get_string('deleted')." $count x $strtable");\r
-               }\r
-       }\r
-}\r
-\r
-function hotpot_user_complete($course, $user, $mod, $hp) {\r
-/// Print a detailed representation of what a  user has done with\r
-/// a given particular instance of this module, for user activity reports.\r
-\r
-       $report = hotpot_user_outline($course, $user, $mod, $hp);\r
-       if (empty($report)) {\r
-               print get_string("noactivity", "hotpot");\r
-       } else {\r
-               $date = userdate($report->time, get_string('strftimerecentfull'));\r
-               print $report->info.' '.get_string('mostrecently').': '.$date;\r
-       }\r
-       return true;\r
-}\r
-\r
-function hotpot_user_outline($course, $user, $mod, $hp) {\r
-/// Return a small object with summary information about what a\r
-/// user has done with a given particular instance of this module\r
-/// Used for user activity reports.\r
-/// $report->time = the time they did it\r
-/// $report->info = a short text description\r
-\r
-       $report = NULL;\r
-       if ($records = get_records_select("hotpot_attempts", "hotpot='$hp->id' AND userid='$user->id'", "timestart ASC", "*")) {\r
-               $scores = array();\r
-               foreach ($records as $record){\r
-                       if (empty($report->time)) {\r
-                               $report->time = $record->timestart;\r
-                       }\r
-                       $scores[] = hotpot_format_score($record);\r
-               }\r
-               if (empty($scores)) {\r
-                       $report->time = 0;\r
-                       $report->info = get_string('noactivity', 'hotpot');\r
-               } else {\r
-                       $report->info = get_string('score', 'quiz').': '.implode(', ', $scores);\r
-               }\r
-       }\r
-       return $report;\r
-}\r
-\r
-function hotpot_format_score($record, $undefined='&nbsp;') {\r
-       if (isset($record->score)) {\r
-               $score = $record->score;\r
-       } else {\r
-               $score = $undefined;\r
-       }\r
-       return $score;\r
-}\r
-\r
-function hotpot_format_status($record, $undefined='&nbsp;') {\r
-       global $HOTPOT_STATUS;\r
-\r
-       if (isset($record->status) || isset($HOTPOT_STATUS[$record->status])) {\r
-               $status = $HOTPOT_STATUS[$record->status];\r
-       } else {\r
-               $status = $undefined;\r
-       }\r
-       return $status;\r
-}\r
-\r
-function hotpot_print_recent_activity($course, $isteacher, $timestart) {\r
-/// Given a course and a time, this module should find recent activity\r
-/// that has occurred in hotpot activities and print it out.\r
-/// Return true if there was output, or false is there was none.\r
-\r
-       global $CFG;\r
-\r
-       $result = false;\r
-       if($isteacher){\r
-\r
-               $records = get_records_sql("\r
-                       SELECT\r
-                               h.id AS id,\r
-                               h.name AS name,\r
-                               COUNT(*) AS count_attempts\r
-                       FROM\r
-                               {$CFG->prefix}hotpot AS h,\r
-                               {$CFG->prefix}hotpot_attempts AS a\r
-                       WHERE\r
-                               h.course = $course->id\r
-                               AND h.id = a.hotpot\r
-                               AND a.id = a.clickreportid\r
-                               AND a.starttime > $timestart\r
-                       GROUP  BY\r
-                               h.id, h.name\r
-               ");\r
-               // note that PostGreSQL requires h.name in the GROUP BY clause\r
-\r
-               if($records) {\r
-\r
-                       $names = array();\r
-                       foreach ($records as $id => $record){\r
-                               $href = "$CFG->wwwroot/mod/hotpot/view.php?hp=$id";\r
-                               $name = '&nbsp;<a href="'.$href.'">'.$record->name.'</a>';\r
-                               if ($record->count_attempts > 1) {\r
-                                       $name .= " ($record->count_attempts)";\r
-                               }\r
-                               $names[] = $name;\r
-                       }\r
-\r
-                       print_headline(get_string('modulenameplural', 'hotpot').':');\r
-\r
-                       if ($CFG->version >= 2005050500) { // Moodle 1.5+\r
-                               echo '<div class="head"><div class="name">'.implode('<br />', $names).'</div></div>';\r
-                       } else { // Moodle 1.4.x (or less)\r
-                               echo '<font size="1">'.implode('<br />', $names).'</font>';\r
-                       }\r
-\r
-                       $result = true;\r
-               }\r
-       }\r
-       return $result;  //  True if anything was printed, otherwise false\r
-}\r
-\r
-function hotpot_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $cmid="", $userid="", $groupid="") {\r
-// Returns all quizzes since a given time.\r
-\r
-       global $CFG;\r
-\r
-       // If $cmid or $userid are specified, then this restricts the results\r
-       $cm_select = empty($cmid) ? "" : " AND cm.id = '$cmid'";\r
-       $user_select = empty($userid) ? "" : " AND u.id = '$userid'";\r
-\r
-       $records = get_records_sql("\r
-               SELECT\r
-                       a.*,\r
-                       h.name, h.course,\r
-                       cm.instance, cm.section,\r
-                       u.firstname, u.lastname, u.picture\r
-               FROM\r
-                       {$CFG->prefix}hotpot_attempts AS a,\r
-                       {$CFG->prefix}hotpot AS h,\r
-                       {$CFG->prefix}course_modules AS cm,\r
-                       {$CFG->prefix}user AS u\r
-               WHERE\r
-                       a.timefinish > '$sincetime'\r
-                       AND a.id = a.clickreportid\r
-                       AND a.userid = u.id $user_select\r
-                       AND a.hotpot = h.id $cm_select\r
-                       AND cm.instance = h.id\r
-                       AND cm.course = '$courseid'\r
-                       AND h.course = cm.course\r
-               ORDER BY\r
-                       a.timefinish ASC\r
-       ");\r
-\r
-       if (!empty($records)) {\r
-               foreach ($records as $record) {\r
-                       if (empty($groupid) || ismember($groupid, $record->userid)) {\r
-\r
-                               unset($activity);\r
-\r
-                               $activity->type = "hotpot";\r
-                               $activity->defaultindex = $index;\r
-                               $activity->instance = $record->hotpot;\r
-\r
-                               $activity->name = $record->name;\r
-                               $activity->section = $record->section;\r
-\r
-                               $activity->content->attemptid = $record->id;\r
-                               $activity->content->attempt = $record->attempt;\r
-                               $activity->content->score = $record->score;\r
-                               $activity->content->timestart = $record->timestart;\r
-                               $activity->content->timefinish = $record->timefinish;\r
-\r
-                               $activity->user->userid = $record->userid;\r
-                               $activity->user->fullname = fullname($record);\r
-                               $activity->user->picture = $record->picture;\r
-\r
-                               $activity->timestamp = $record->timefinish;\r
-\r
-                               $activities[] = $activity;\r
-\r
-                               $index++;\r
-                       }\r
-               } // end foreach\r
-       }\r
-}\r
-\r
-function hotpot_print_recent_mod_activity($activity, $course, $detail=false) {\r
-/// Basically, this function prints the results of "hotpot_get_recent_activity"\r
-\r
-       global $CFG, $THEME, $USER;\r
-\r
-       print '<table border="0" cellpadding="3" cellspacing="0">';\r
-\r
-       print '<tr><td bgcolor="'.$THEME->cellcontent2.'" class="forumpostpicture" width="35" valign="top">';\r
-       print_user_picture($activity->user->userid, $course, $activity->user->picture);\r
-       print '</td><td width="100%"><font size="2">';\r
-\r
-       if ($detail) {\r
-               // activity icon\r
-               $src = "$CFG->modpixpath/$activity->type/icon.gif";\r
-               print '<img src="'.$src.'" height="16" width="16" alt="'.$activity->type.'" /> ';\r
-\r
-               // link to activity\r
-               $href = "$CFG->wwwroot/mod/hotpot/view.php?hp=$activity->instance";\r
-               print '<a href="'.$href.'">'.$activity->name.'</a> - ';\r
-       }\r
-       if (isteacher($course)) {\r
-               // score (with link to attempt details)\r
-               $href = "$CFG->wwwroot/mod/hotpot/review.php?hp=$activity->instance&attempt=".$activity->content->attemptid;\r
-               print '<a href="'.$href.'">('.hotpot_format_score($activity->content).')</a> ';\r
-\r
-               // attempt number\r
-               print get_string('attempt', 'quiz').' - '.$activity->content->attempt.'<br />';\r
-       }\r
-\r
-       // link to user\r
-       $href = "$CFG->wwwroot/user/view.php?id=$activity->user->userid&course=$course";\r
-       print '<a href="'.$href.'">'.$activity->user->fullname.'</a> ';\r
-\r
-       // time and date\r
-       print ' - ' . userdate($activity->timestamp);\r
-\r
-       // duration\r
-       $duration = format_time($activity->content->timestart - $activity->content->timefinish);\r
-       print " &nbsp; ($duration)";\r
-\r
-       print "</font></td></tr>";\r
-       print "</table>";\r
-}\r
-\r
-function hotpot_cron () {\r
-/// Function to be run periodically according to the moodle cron\r
-/// This function searches for things that need to be done, such\r
-/// as sending out mail, toggling flags etc ...\r
-\r
-       global $CFG;\r
-\r
-       return true;\r
-}\r
-\r
-function hotpot_grades($hotpotid) {\r
-/// Must return an array of grades for a given instance of this module,\r
-/// indexed by user.  It also returns a maximum allowed grade.\r
-\r
-       $hotpot = get_record('hotpot', 'id', $hotpotid);\r
-       $return->grades = hotpot_get_grades($hotpot);\r
-       $return->maxgrade = $hotpot->grade;\r
-\r
-       return $return;\r
-}\r
-function hotpot_get_grades($hotpot, $user_ids='') {\r
-       global $CFG;\r
-\r
-       $grades = array();\r
-\r
-       $weighting = $hotpot->grade / 100;\r
-       $precision = hotpot_get_precision($hotpot);\r
-\r
-       // set the SQL string to determine the $grade\r
-       $grade = "";\r
-       switch ($hotpot->grademethod) {\r
-               case HOTPOT_GRADEMETHOD_HIGHEST:\r
-                       $grade = "ROUND(MAX(score) * $weighting, $precision) AS grade";\r
-                       break;\r
-               case HOTPOT_GRADEMETHOD_AVERAGE:\r
-                       // the 'AVG' function skips abandoned quizzes, so use SUM(score)/COUNT(id)\r
-                       $grade = "ROUND(SUM(score)/COUNT(id) * $weighting, $precision) AS grade";\r
-                       break;\r
-               case HOTPOT_GRADEMETHOD_FIRST:\r
-                       if ($CFG->dbtype=='postgres7') {\r
-                               $grade = "MIN(timestart||'_'||(CASE WHEN (score IS NULL) THEN '' ELSE TRIM(ROUND(score * $weighting, $precision)) END)) AS grade";\r
-                       } else {\r
-                               $grade = "MIN(CONCAT(timestart, '_', IF(score IS NULL, '', ROUND(score * $weighting, $precision)))) AS grade";\r
-                       }\r
-                       break;\r
-               case HOTPOT_GRADEMETHOD_LAST:\r
-                       if ($CFG->dbtype=='postgres7') {\r
-                               $grade = "MAX(timestart||'_'||(CASE WHEN (score IS NULL) THEN '' ELSE TRIM(ROUND(score * $weighting, $precision)) END)) AS grade";\r
-                       } else {\r
-                               $grade = "MAX(CONCAT(timestart, '_', IF(score IS NULL, '', ROUND(score * $weighting, $precision)))) AS grade";\r
-                       }\r
-                       break;\r
-       }\r
-\r
-       if ($grade) {\r
-               $userid_condition = empty($user_ids) ? '' : "AND userid IN ($user_ids) ";\r
-               $grades = get_records_sql_menu("\r
-                       SELECT userid, $grade\r
-                       FROM {$CFG->prefix}hotpot_attempts\r
-                       WHERE timefinish>0 AND hotpot='$hotpot->id' $userid_condition\r
-                       GROUP BY userid\r
-               ");\r
-               if ($grades) {\r
-                       if ($hotpot->grademethod==HOTPOT_GRADEMETHOD_FIRST || $hotpot->grademethod==HOTPOT_GRADEMETHOD_LAST) {\r
-                               // remove left hand characters in $grade (up to and including the underscore)\r
-                               foreach ($grades as $userid=>$grade) {\r
-                                       $grades[$userid] = substr($grades[$userid], strpos($grades[$userid], '_')+1);\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       return $grades;\r
-}\r
-function hotpot_get_precision(&$hotpot) {\r
-       return ($hotpot->grademethod==HOTPOT_GRADEMETHOD_AVERAGE || $hotpot->grade<100) ? 1 : 0;\r
-}\r
-\r
-function hotpot_get_participants($hotpotid) {\r
-//Must return an array of user ids who are participants\r
-//for a given instance of hotpot. Must include every user involved\r
-//in the instance, independient of his role (student, teacher, admin...)\r
-//See other modules as example.\r
-       global $CFG;\r
-\r
-       return get_records_sql("\r
-               SELECT DISTINCT\r
-                       u.id, u.id\r
-               FROM\r
-                       {$CFG->prefix}user u,\r
-                       {$CFG->prefix}hotpot_attempts a\r
-               WHERE\r
-                       u.id = a.userid\r
-                       AND a.hotpot = '$hotpotid'\r
-       ");\r
-}\r
-\r
-function hotpot_scale_used ($hotpotid, $scaleid) {\r
-//This function returns if a scale is being used by one hotpot\r
-//it it has support for grading and scales. Commented code should be\r
-//modified if necessary. See forum, glossary or journal modules\r
-//as reference.\r
-\r
-       $report = false;\r
-\r
-       //$rec = get_record("hotpot","id","$hotpotid","scale","-$scaleid");\r
-       //\r
-       //if (!empty($rec)  && !empty($scaleid)) {\r
-       //      $report = true;\r
-       //}\r
-\r
-       return $report;\r
-}\r
-\r
-//////////////////////////////////////////////////////////\r
-/// Any other hotpot functions go here.\r
-/// Each of them must have a name that starts with hotpot\r
-\r
-\r
-function hotpot_add_attempt($hotpotid) {\r
-       global $db, $CFG, $USER;\r
-       $time = time();\r
-       switch (strtolower($CFG->dbtype)) {\r
-               case 'mysql':\r
-                       $timefinish = "IF(a.timefinish IS NULL, '$time', a.timefinish)";\r
-                       $clickreportid = "IF(a.clickreportid IS NULL, a.id, a.clickreportid)";\r
-                       break;\r
-               case 'postgres7':\r
-                       $timefinish = "WHEN(a.timefinish IS NULL) THEN '$time' ELSE a.timefinish";\r
-                       $clickreportid = "WHEN(a.clickreportid IS NULL) THEN a.id ELSE a.clickreportid";\r
-                       break;\r
-       }\r
-\r
-       // set all previous "in progress" attempts at this quiz to "abandoned"\r
-       $db->Execute("\r
-               UPDATE\r
-                       {$CFG->prefix}hotpot_attempts as a\r
-               SET\r
-                       a.timefinish = $timefinish,\r
-                       a.status = '".HOTPOT_STATUS_ABANDONED."',\r
-                       a.clickreportid = $clickreportid\r
-               WHERE\r
-                       a.hotpot='$hotpotid'\r
-                       AND a.userid='$USER->id'\r
-                       AND a.status='".HOTPOT_STATUS_INPROGRESS."'\r
-       ");\r
-\r
-       // create and add new attempt record\r
-       $attempt->hotpot = $hotpotid;\r
-       $attempt->userid = $USER->id;\r
-       $attempt->attempt = hotpot_get_next_attempt($hotpotid);\r
-       $attempt->timestart = time();\r
-\r
-       return insert_record("hotpot_attempts", $attempt);\r
-}\r
-function hotpot_get_next_attempt($hotpotid) {\r
-       global $USER;\r
-\r
-       // get max attempt so far\r
-       $i = count_records_select('hotpot_attempts', "hotpot='$hotpotid' AND userid='$USER->id'", 'MAX(attempt)');\r
-\r
-       return empty($i) ? 1 : ($i+1);\r
-}\r
-function hotpot_get_question_name($question) {\r
-       $name = '';\r
-       if (isset($question->text)) {\r
-               $name = hotpot_strings($question->text);\r
-       }\r
-       if (empty($name)) {\r
-               $name = $question->name;\r
-       }\r
-       return $name;\r
-}\r
-function hotpot_strings($ids) {\r
-\r
-       // array of ids of empty strings\r
-       static $HOTPOT_EMPTYSTRINGS;\r
-\r
-       if (!isset($HOTPOT_EMPTYSTRINGS)) { // first time only\r
-               // get ids of empty strings\r
-               $emptystrings = get_records_select('hotpot_strings', 'LENGTH(TRIM(string))=0');\r
-               $HOTPOT_EMPTYSTRINGS = empty($emptystrings) ? array() : array_keys($emptystrings);\r
-       }\r
-\r
-       $strings = array();\r
-       if (!empty($ids)) {\r
-               $ids = explode(',', $ids);\r
-               foreach ($ids as $id) {\r
-                       if (!in_array($id, $HOTPOT_EMPTYSTRINGS)) {\r
-                               $strings[] = hotpot_string($id);\r
-                       }\r
-               }\r
-       }\r
-       return implode(',', $strings);\r
-}\r
-function hotpot_string($id) {\r
-       return get_field('hotpot_strings', 'string', 'id', $id);\r
-}\r
-\r
-//////////////////////////////////////////////////////////////////////////////////////\r
-/// the class definitions to handle XML trees\r
-\r
-// get the standard XML parser supplied with Moodle\r
-require_once("$CFG->libdir/xmlize.php");\r
-\r
-// get the default class for hotpot quiz templates\r
-require_once("$CFG->hotpottemplate/default.php");\r
-\r
-class hotpot_xml_tree {\r
-       function hotpot_xml_tree($str, $xml_root='') {\r
-               if (empty($str)) {\r
-                       $this->xml =  array();\r
-               } else {\r
-                       if (empty($CFG->unicodedb)) {\r
-                               $str = utf8_encode($str);\r
-                       }\r
-                       $this->xml =  xmlize($str, 0);\r
-               }\r
-               $this->xml_root = $xml_root;\r
-       }\r
-       function xml_value($tags, $more_tags="[0]['#']") {\r
-\r
-               $tags = empty($tags) ? '' : "['".str_replace(",", "'][0]['#']['", $tags)."']";\r
-               eval('$value = &$this->xml'.$this->xml_root.$tags.$more_tags.';');\r
-\r
-               if (is_string($value)) {\r
-                       if (empty($CFG->unicodedb)) {\r
-                               $value = utf8_decode($value);\r
-                       }\r
-\r
-                       // decode angle brackets\r
-                       $value = strtr($value, array('&#x003C;'=>'<', '&#x003E;'=>'>', '&#x0026;'=>'&'));\r
-\r
-                       // remove white space between <table>, <ul|OL|DL> and <OBJECT|EMBED> parts\r
-                       // (so it doesn't get converted to <br />)\r
-                       $htmltags = '('\r
-                       .       'TABLE|/?CAPTION|/?COL|/?COLGROUP|/?TBODY|/?TFOOT|/?THEAD|/?TD|/?TH|/?TR'\r
-                       .       '|OL|UL|/?LI'\r
-                       .       '|DL|/?DT|/?DD'\r
-                       .       '|EMBED|OBJECT|APPLET|/?PARAM'\r
-                       //.     '|SELECT|/?OPTION'\r
-                       //.     '|FIELDSET|/?LEGEND'\r
-                       //.     '|FRAMESET|/?FRAME'\r
-                       .       ')'\r
-                       ;\r
-                       $search = '#(<'.$htmltags.'[^>]*'.'>)\s+'.'(?='.'<'.')#is';\r
-                       $value = preg_replace($search, '\\1', $value);\r
-\r
-                       // replace remaining newlines with <br />\r
-                       $value = str_replace("\n", '<br />', $value);\r
-\r
-                       // encode unicode characters as HTML entities\r
-                       // (in particular, accented charaters that have not been encoded by HP)\r
-\r
-                       // unicode characters can be detected by checking the hex value of a character\r
-                       //      00 - 7F : ascii char (roman alphabet + punctuation)\r
-                       //      80 - BF : byte 2, 3 or 4 of a unicode char\r
-                       //      C0 - DF : 1st byte of 2-byte char\r
-                       //      E0 - EF : 1st byte of 3-byte char\r
-                       //      F0 - FF : 1st byte of 4-byte char\r
-                       // if the string doesn't match the above, it might be\r
-                       //      80 - FF : single-byte, non-ascii char\r
-                       $search = '#('.'[\xc0-\xdf][\x80-\xbf]'.'|'.'[\xe0-\xef][\x80-\xbf]{2}'.'|'.'[\xf0-\xff][\x80-\xbf]{3}'.'|'.'[\x80-\xff]'.')#se';\r
-                       $value = preg_replace($search, "hotpot_utf8_to_html_entity('\\1')", $value);\r
-               }\r
-               return $value;\r
-       }\r
-       function xml_values($tags) {\r
-               $i = 0;\r
-               $values = array();\r
-               while ($value = $this->xml_value($tags, "[$i]['#']")) {\r
-                       $values[$i++] = $value;\r
-               }\r
-               return $values;\r
-       }\r
-       function obj_value(&$obj, $name) {\r
-               return is_object($obj) ? @$obj->$name : (is_array($obj) ? @$obj[$name] : NULL);\r
-       }\r
-       function encode_cdata(&$str, $tag) {\r
-\r
-               // conversion tables\r
-               static $HTML_ENTITIES = array(\r
-                       '&apos;' => "'",\r
-                       '&quot;' => '"',\r
-                       '&lt;'   => '<',\r
-                       '&gt;'   => '>',\r
-                       '&amp;'  => '&',\r
-               );\r
-               static $ILLEGAL_STRINGS = array(\r
-                       "\r"  => '',\r
-                       "\n"  => '&lt;br /&gt;',\r
-                       ']]>' => '&#93;&#93;&#62;',\r
-               );\r
-\r
-               // extract the $tag from the $str(ing), if possible\r
-               $pattern = '|(^.*<'.$tag.'[^>]*)(>.*<)(/'.$tag.'>.*$)|is';\r
-               if (preg_match($pattern, $str, $matches)) {\r
-\r
-                       // encode problematic CDATA chars and strings\r
-                       $matches[2] = strtr($matches[2], $ILLEGAL_STRINGS);\r
-\r
-                       // if there are any ampersands in "open text"\r
-                       // surround them by CDATA start and end markers\r
-                       // (and convert HTML entities to plain text)\r
-                       $search = '/>([^<]*&[^<]*)</e';\r
-                       $replace = '"><![CDATA[".strtr("$1", $HTML_ENTITIES)."]]><"';\r
-                       $matches[2] = preg_replace($search, $replace, $matches[2]);\r
-\r
-                       $str = $matches[1].$matches[2].$matches[3];\r
-               }\r
-       }\r
-}\r
-\r
-class hotpot_xml_quiz extends hotpot_xml_tree {\r
-\r
-       // constructor function\r
-       function hotpot_xml_quiz(&$obj, $read_file=true, $parse_xml=true, $convert_urls=true, $report_errors=true, $create_html=true) {\r
-               // obj can be the $_GET array or a form object/array\r
-\r
-               global $CFG, $HOTPOT_OUTPUTFORMAT, $HOTPOT_OUTPUTFORMAT_DIR;\r
-\r
-               // check xmlize functions are available\r
-               if (! function_exists("xmlize")) {\r
-                       error('xmlize functions are not available');\r
-               }\r
-\r
-               $this->read_file = $read_file;\r
-               $this->parse_xml = $parse_xml;\r
-               $this->convert_urls = $convert_urls;\r
-               $this->report_errors = $report_errors;\r
-               $this->create_html = $create_html;\r
-\r
-               // extract fields from $obj\r
-               //      course     : the course id\r
-               //      reference       : the filename within the files folder\r
-               //      location         : "site" files folder or "course" files folder\r
-               //      navigation   : type of navigation required in quiz\r
-               //      forceplugins : force Moodle compatible media players\r
-               $this->course = $this->obj_value($obj, 'course');\r
-               $this->reference = $this->obj_value($obj, 'reference');\r
-               $this->location = $this->obj_value($obj, 'location');\r
-               $this->navigation = $this->obj_value($obj, 'navigation');\r
-               $this->forceplugins = $this->obj_value($obj, 'forceplugins');\r
-\r
-               // can't continue if there is no course or reference\r
-               if (empty($this->course) || empty($this->reference)) {\r
-                       $this->error = get_string('error_nocourseorfilename', 'hotpot');\r
-                       if ($this->report_errors) {\r
-                               error($this->error);\r
-                       }\r
-                       return;\r
-               }\r
-\r
-               $this->course_homeurl = "$CFG->wwwroot/course/view.php?id=$this->course";\r
-\r
-               // set filedir, filename and filepath\r
-               switch ($this->location) {\r
-                       case HOTPOT_LOCATION_SITEFILES:\r
-                               $site = get_site();\r
-                               $this->filedir = $site->id;\r
-                               break;\r
-\r
-                       case HOTPOT_LOCATION_COURSEFILES:\r
-                       default:\r
-                               $this->filedir = $this->course;\r
-                               break;\r
-               }\r
-               $this->filesubdir = dirname($this->reference);\r
-               if ($this->filesubdir=='.') {\r
-                       $this->filesubdir = '';\r
-               }\r
-               if ($this->filesubdir) {\r
-                       $this->filesubdir .= '/';\r
-               }\r
-               $this->filename = basename($this->reference);\r
-               $this->fileroot = "$CFG->dataroot/$this->filedir";\r
-               $this->filepath = "$this->fileroot/$this->reference";\r
-\r
-               // read the file, if required\r
-               if ($this->read_file) {\r
-\r
-                       if (!file_exists($this->filepath) || !is_readable($this->filepath)) {\r
-                               $this->error = get_string('error_couldnotopensourcefile', 'hotpot', $this->filepath);\r
-                               if ($this->report_errors) {\r
-                                       error($this->error, $this->course_homeurl);\r
-                               }\r
-                               return;\r
-                       }\r
-\r
-                       // read in the XML source\r
-                       $this->source = file_get_contents($this->filepath);\r
-\r
-                       // convert relative URLs to absolute URLs\r
-                       if ($this->convert_urls) {\r
-                               $this->hotpot_convert_relative_urls($this->source);\r
-                       }\r
-\r
-                       $this->html = '';\r
-                       $this->quiztype = '';\r
-                       $this->outputformat = 0;\r
-\r
-                       // is this an html file?\r
-                       if (preg_match('|\.html?$|', $this->filename)) {\r
-\r
-                               $this->filetype = 'html';\r
-                               $this->html = &$this->source;\r
-\r
-                               // relative URLs in stylesheets\r
-                               $search = '|'.'(<style[^>]*>)'.'(.*?)'.'(</style>)'.'|ise';\r
-                               $replace = "stripslashes('\\1').hotpot_convert_stylesheets_urls('".$this->get_baseurl()."','".$this->reference."','\\2'.'\\3')";\r
-                               $this->source = preg_replace($search, $replace, $this->source);\r
-\r
-                               // relative URLs in "PreloadImages(...);"\r
-                               $search = '|'.'(?<='.'PreloadImages'.'\('.')'."([^)]+?)".'(?='.'\);'.')'.'|se';\r
-                               $replace = "hotpot_convert_preloadimages_urls('".$this->get_baseurl()."','".$this->reference."','\\1')";\r
-                               $this->source = preg_replace($search, $replace, $this->source);\r
-\r
-                               // relative URLs in <button class="NavButton" ... onclick="location='...'">\r
-                               $search = '|'.'(?<='.'onclick="'."location='".')'."([^']*)".'(?='."'; return false;".'")'.'|ise';\r
-                               $replace = "hotpot_convert_navbutton_url('".$this->get_baseurl()."','".$this->reference."','\\1','".$this->course."')";\r
-                               $this->source = preg_replace($search, $replace, $this->source);\r
-\r
-                       } else {\r
-                               if ($this->parse_xml) {\r
-\r
-                                       $this->filetype = 'xml';\r
-\r
-                                       // encode "gap fill" text in JCloze exercise\r
-                                       $this->encode_cdata($this->source, 'gap-fill');\r
-\r
-                                       // convert source to xml tree\r
-                                       $this->hotpot_xml_tree($this->source);\r
-\r
-                                       $keys = array_keys($this->xml);\r
-                                       foreach ($keys as $key) {\r
-                                               if (preg_match('/^(hotpot|textoys)-(\w+)-file$/i', $key, $matches)) {\r
-                                                       $this->quiztype = strtolower($matches[2]);\r
-                                                       $this->xml_root = "['$key']['#']";\r
-                                                       break;\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               if ($this->create_html) {\r
-\r
-                                       // set the real output format from the requested output format\r
-                                       $this->real_outputformat = $this->obj_value($obj, 'outputformat');\r
-                                       $this->draganddrop = '';\r
-                                       if (\r
-                                               empty($this->real_outputformat) ||\r
-                                               $this->real_outputformat==HOTPOT_OUTPUTFORMAT_BEST ||\r
-                                               empty($HOTPOT_OUTPUTFORMAT_DIR[$this->real_outputformat])\r
-                                       ) {\r
-                                               if ($CFG->hotpotismobile && isset($HOTPOT_OUTPUTFORMAT_DIR[HOTPOT_OUTPUTFORMAT_MOBILE])) {\r
-                                                               $this->real_outputformat = HOTPOT_OUTPUTFORMAT_MOBILE;\r
-                                               } else { // PC\r
-                                                       if ($this->quiztype=='jmatch' || $this->quiztype=='jmix') {\r
-                                                               $this->real_outputformat = HOTPOT_OUTPUTFORMAT_V6_PLUS;\r
-                                                       } else {\r
-                                                               $this->real_outputformat = HOTPOT_OUTPUTFORMAT_V6;\r
-                                                       }\r
-                                               }\r
-                                       }\r
-\r
-                                       if ($this->real_outputformat==HOTPOT_OUTPUTFORMAT_V6_PLUS) {\r
-                                               if ($this->quiztype=='jmatch' || $this->quiztype=='jmix') {\r
-                                                       $this->draganddrop = 'd'; // prefix for templates (can also be "f" ?)\r
-                                               }\r
-                                               $this->real_outputformat = HOTPOT_OUTPUTFORMAT_V6;\r
-                                       }\r
-\r
-                                       // set path(s) to template\r
-                                       $this->template_dir = $HOTPOT_OUTPUTFORMAT_DIR[$this->real_outputformat];\r
-                                       $this->template_dirpath = $CFG->hotpottemplate.'/'.$this->template_dir;\r
-                                       $this->template_filepath = $CFG->hotpottemplate.'/'.$this->template_dir.'.php';\r
-\r
-                                       // check template class exists\r
-                                       if (!file_exists($this->template_filepath) || !is_readable($this->template_filepath)) {\r
-                                               $this->error = get_string('error_couldnotopentemplate', 'hotpot', $this->template_dir);\r
-                                               if ($this->report_errors) {\r
-                                                       error($this->error, $this->course_homeurl);\r
-                                               }\r
-                                               return;\r
-                                       }\r
-\r
-                                       // get default and output-specfic template classes\r
-                                       include($this->template_filepath);\r
-\r
-                                       // create html (using the template for the specified output format)\r
-                                       $this->template = new hotpot_xml_quiz_template($this);\r
-                                       $this->html = &$this->template->html;\r
-\r
-                               } // end $this->create_html\r
-                       } // end if html/xml file\r
-               } // end if $this->read_file\r
-       } // end constructor function\r
-\r
-       function hotpot_convert_relative_urls(&$str) {\r
-               $tagopen = '(?:(<)|(&lt;)|(&amp;#x003C;))'; // left angle bracket\r
-               $tagclose = '(?(2)>|(?(3)&gt;|(?(4)&amp;#x003E;)))'; //  right angle bracket (to match left angle bracket)\r
-\r
-               $space = '\s+'; // at least one space\r
-               $anychar = '(?:[^>]*?)'; // any character\r
-\r
-               $quoteopen = '("|&quot;|&amp;quot;)'; // open quote\r
-               $quoteclose = '\\5'; //  close quote (to match open quote)\r
-\r
-               $replace = "hotpot_convert_relative_url('".$this->get_baseurl()."', '".$this->reference."', '\\1', '\\6', '\\7')";\r
-\r
-               $tags = array('script'=>'src', 'link'=>'href', 'a'=>'href','img'=>'src','param'=>'value', 'object'=>'data', 'embed'=>'src');\r
-               foreach ($tags as $tag=>$attribute) {\r
-                       if ($tag=='param') {\r
-                               $url = '\S+?\.\S+?'; // must include a filename and have no spaces\r
-                       } else {\r
-                               $url = '.*?';\r
-                       }\r
-                       $search = "%($tagopen$tag$space$anychar$attribute=$quoteopen)($url)($quoteclose$anychar$tagclose)%ise";\r
-                       $str = preg_replace($search, $replace, $str);\r
-               }\r
-       }\r
-\r
-       function get_baseurl() {\r
-               // set the url base (first time only)\r
-               if (!isset($this->baseurl)) {\r
-                       global $CFG;\r
-                       if ($CFG->slasharguments) {\r
-                               $this->baseurl = "$CFG->wwwroot/file.php/$this->filedir/";\r
-                       } else {\r
-                               $this->baseurl = "$CFG->wwwroot/file.php?file=/$this->filedir/";\r
-                       }\r
-               }\r
-               return $this->baseurl;\r
-       }\r
-\r
-\r
-       // insert forms and messages\r
-\r
-       function remove_nav_buttons() {\r
-               $search = '#<!-- Begin(Top|Bottom)NavButtons -->(.*?)<!-- End(Top|Bottom)NavButtons -->#s';\r
-               $this->html = preg_replace($search, '', $this->html);\r
-       }\r
-       function insert_script($src=HOTPOT_JS) {\r
-               $script = '<script src="'.$src.'" type="text/javascript" language="javascript"></script>'."\n";\r
-               $this->html = preg_replace('|</head>|i', $script.'</head>', $this->html, 1);\r
-       }\r
-       function insert_submission_form($attemptid, $startblock, $endblock, $keep_contents=false) {\r
-               $form_name = 'store';\r
-               $form_fields = ''\r
-               .       '<input type="hidden" name="attemptid" value="'.$attemptid.'" />'\r
-               .       '<input type="hidden" name="starttime" value="" />'\r
-               .       '<input type="hidden" name="endtime" value="" />'\r
-               .       '<input type="hidden" name="mark" value="" />'\r
-               .       '<input type="hidden" name="detail" value="" />'\r
-               .       '<input type="hidden" name="status" value="" />'\r
-               ;\r
-               $this->insert_form($startblock, $endblock, $form_name, $form_fields, $keep_contents);\r
-       }\r
-       function insert_giveup_form($attemptid, $startblock, $endblock, $keep_contents=false) {\r
-               $form_name = 'giveup';\r
-               $form_fields = ''\r
-               .       '<input type="hidden" name="attemptid" value="'.$attemptid.'" />'\r
-               .       '<input type="hidden" name="status" value="'.HOTPOT_STATUS_ABANDONED.'" />'\r
-               .       '<input type="submit" value="'.get_string('giveup', 'hotpot').'" class="FuncButton" onfocus="FuncBtnOver(this)" onblur="FuncBtnOut(this)" onmouseover="FuncBtnOver(this)" onmouseout="FuncBtnOut(this)" onmousedown="FuncBtnDown(this)" onmouseup="FuncBtnOut(this)" />'\r
-               ;\r
-               $this->insert_form($startblock, $endblock, $form_name, $form_fields, $keep_contents, true);\r
-       }\r
-       function insert_form($startblock, $endblock, $form_name, $form_fields, $keep_contents, $center=false) {\r
-               global $CFG;\r
-               $search = '#('.preg_quote($startblock).')(.*?)('.preg_quote($endblock).')#s';\r
-               $replace = $form_fields;\r
-               if ($keep_contents) {\r
-                       $replace .= '\\2';\r
-               }\r
-               if ($form_name) {\r
-                       $replace = '<form action="'.$CFG->wwwroot.'/mod/hotpot/attempt.php" method="POST" name="'.$form_name.'" target="'.$CFG->framename.'">'.$replace.'</form>';\r
-               }\r
-               if ($center) {\r
-                       $replace = '<div style="margin-left:auto; margin-right:auto; text-align: center;">'.$replace.'</div>';\r
-               }\r
-               $replace = '\\1'.$replace.'\\3';\r
-               $this->html = preg_replace($search, $replace, $this->html, 1);\r
-       }\r
-       function insert_message($start_str, $message, $color='red', $align='center') {\r
-               $message = '<p align="'.$align.'" style="text-align:'.$align.'"><b><font color="'.$color.'">'.$message."</font></b></p>\n";\r
-               $this->html = preg_replace('|'.preg_quote($start_str).'|', $start_str.$message, $this->html, 1);\r
-       }\r
-\r
-       function adjust_media_urls() {\r
-\r
-               if ($this->forceplugins) {\r
-\r
-                       // make sure the Moodle media plugin is available\r
-                       global $CFG;\r
-                       include_once "$CFG->dirroot/filter/mediaplugin/filter.php";\r
-\r
-                       // exclude swf files from the filter\r
-                       //$CFG->filter_mediaplugin_ignore_swf = true;\r
-\r
-                       $space = '\s(?:.+\s)?';\r
-                       $quote = '["'."']?"; // single, double, or no quote\r
-\r
-                       // patterns to media files types and paths\r
-                       $filetype = "avi|mpeg|mpg|mp3|mov|wmv";\r
-                       $filepath = ".*?\.($filetype)";\r
-\r
-                       $tagopen = '(?:(<)|(\\\\u003C))'; // left angle-bracket (uses two parenthese)\r
-                       $tagclose = '(?(1)>|(?(2)\\\\u003E))'; // right angle-bracket (to match the left one)\r
-                       $tagreopen = '(?(1)<|(?(2)\\\\u003C))'; // another left angle-bracket (to match the first one)\r
-\r
-                       // pattern to match <PARAM> tags which contain the file path\r
-                       //      wmp        : url\r
-                       //      quicktime  : src\r
-                       //      realplayer : src\r
-                       //      flash      : movie (doesn't need replacing)\r
-                       $param_url = "/{$tagopen}param{$space}name=$quote(?:movie|src|url)$quote{$space}value=$quote($filepath)$quote.*?$tagclose/is";\r
-\r
-                       // pattern to match <a> tags which link to multimedia files\r
-                       $link_url = "/{$tagopen}a{$space}href=$quote($filepath)$quote.*?$tagclose.*?$tagreopen\/A$tagclose/is";\r
-\r
-                       // extract <object> tags\r
-                       preg_match_all("/{$tagopen}object\s.*?{$tagclose}(.*?){$tagreopen}\/object{$tagclose}/is", $this->html, $objects);\r
-\r
-                       $i_max = count($objects[0]);\r
-                       for ($i=0; $i<$i_max; $i++) {\r
-\r
-                               // extract URL from <PARAM> or <A> \r
-                               $url = '';\r
-                               if (preg_match($param_url, $objects[3][$i], $matches) || preg_match($link_url, $objects[3][$i], $matches)) {\r
-                                       $url = $matches[3];\r
-                               }\r
-\r
-                               if ($url) {\r
-                                       // strip inner tags (e.g. <embed>)\r
-                                       $txt = preg_replace("/$tagopen.*?$tagclose/", '', $objects[3][$i]);\r
-\r
-                                       // if url is in the query string, remove the leading characters\r
-                                       $url = preg_replace('/^[^?]*\?([^=]+=[^&]*&)*[^=]+=([^&]*)$/', '$2', $url, 1);\r
-                                       $link = '<a href="'.$url.'">'.$txt.'</a>';\r
-\r
-                                       $new_object = mediaplugin_filter($this->filedir, $link);\r
-                                       $new_object = str_replace($link, '', $new_object);\r
-                                       $new_object = str_replace('&amp;', '&', $new_object);\r
-\r
-                                       $this->html = str_replace($objects[0][$i], $new_object, $this->html);\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-} // end class\r
-\r
-function hotpot_convert_stylesheets_urls($baseurl, $reference, $css, $stripslashes=true) {\r
-       if ($stripslashes) {\r
-               $css = stripslashes($css);\r
-       }\r
-       $search = '|'.'(?<='.'url'.'\('.')'."(.+?)".'(?='.'\)'.')'.'|ise';\r
-       $replace = "hotpot_convert_url('".$baseurl."','".$reference."','\\1')";\r
-       return preg_replace($search, $replace, $css);\r
-}\r
-function hotpot_convert_preloadimages_urls($baseurl, $reference, $urls, $stripslashes=true) {\r
-       if ($stripslashes) {\r
-               $urls = stripslashes($urls);\r
-       }\r
-       $search = '|(?<=["'."'])([^,'".'"]*?)(?=["'."'])|ise";\r
-       $replace = "hotpot_convert_url('".$baseurl."','".$reference."','\\1')";\r
-       return preg_replace($search, $replace, $urls);\r
-}\r
-function hotpot_convert_navbutton_url($baseurl, $reference, $url, $course, $stripslashes=true) {\r
-       global $CFG;\r
-\r
-       if ($stripslashes) {\r
-               $url = stripslashes($url);\r
-       }\r
-       $url = hotpot_convert_url($baseurl, $reference, $url, false);\r
-\r
-       // is this a $url for another hotpot in this course ?\r
-       if (preg_match("|^$baseurl(.*)$|", $url, $matches)) {\r
-               if ($records = get_records_select('hotpot', "course='$course' AND reference='".$matches[1]."'")) {\r
-                       $ids = array_keys($records);\r
-                       $url = "$CFG->wwwroot/mod/hotpot/view.php?hp=".$ids[0];\r
-               }\r
-       }\r
-\r
-       return $url;\r
-}\r
-\r
-function hotpot_convert_relative_url($baseurl, $reference, $opentag, $url, $closetag, $stripslashes=true) {\r
-       if ($stripslashes) {\r
-               $opentag = stripslashes($opentag);\r
-               $url = stripslashes($url);\r
-               $closetag = stripslashes($closetag);\r
-       }\r
-\r
-       // catch <PARAM name="FlashVars" value="TheSound=soundfile.mp3">\r
-       //      ampersands can appear as "&", "&amp;" or "&amp;#x0026;amp;"\r
-       if (preg_match('|^'.'\w+=[^&]+'.'('.'&((amp;#x0026;)?amp;)?'.'\w+=[^&]+)*'.'$|', $url)) {\r
-               $query = $url;\r
-               $url = '';\r
-               $fragment = '';\r
-\r
-       // parse the $url into $matches\r
-       //      [1] path\r
-       //      [2] query string, if any\r
-       //      [3] anchor fragment, if any\r
-       } else if (preg_match('|^'.'([^?]*)'.'((?:\\?[^#]*)?)'.'((?:#.*)?)'.'$|', $url, $matches)) {\r
-               $url = $matches[1];\r
-               $query = $matches[2];\r
-               $fragment = $matches[3];\r
-\r
-       // these appears to be no query or fragment in this url\r
-       } else {\r
-               $query = '';\r
-               $fragment = '';\r
-       }\r
-\r
-       if ($url) {\r
-               $url = hotpot_convert_url($baseurl, $reference, $url, false);\r
-       }\r
-\r
-       if ($query) {\r
-               $search = '#'.'(file|src|thesound)='."([^&]+)".'#ise';\r
-               $replace = "'\\1='.hotpot_convert_url('".$baseurl."','".$reference."','\\2')";\r
-               $query = preg_replace($search, $replace, $query);\r
-       }\r
-\r
-       $url = $opentag.$url.$query.$fragment.$closetag;\r
-\r
-       return $url;\r
-}\r
-\r
-function hotpot_convert_url($baseurl, $reference, $url, $stripslashes=true) {\r
-       // maintain a cache of converted urls\r
-       static $HOTPOT_RELATIVE_URLS = array();\r
-\r
-       if ($stripslashes) {\r
-               $url = stripslashes($url);\r
-       }\r
-\r
-       // is this an absolute url? (or javascript pseudo url)\r
-       if (preg_match('%^(http://|/|javascript:)%i', $url)) {\r
-               // do nothing\r
-\r
-       // has this relative url already been converted?\r
-       } else if (isset($HOTPOT_RELATIVE_URLS[$url])) {\r
-               $url = $HOTPOT_RELATIVE_URLS[$url];\r
-\r
-       } else {\r
-               $relativeurl = $url;\r
-\r
-               // get the subdirectory, $dir, of the quiz $reference\r
-               $dir = dirname($reference);\r
-\r
-               // allow for leading "./" and "../"\r
-               while (preg_match('|^(\.{1,2})/(.*)$|', $url, $matches)) {\r
-                       if ($matches[1]=='..') {\r
-                               $dir = dirname($dir);\r
-                       }\r
-                       $url = $matches[2];\r
-               }\r
-\r
-               // add subdirectory, $dir, to $baseurl, if necessary\r
-               if ($dir && $dir<>'.') {\r
-                       $baseurl .= "$dir/";\r
-               }\r
-\r
-               // prefix $url with $baseurl\r
-               $url = "$baseurl$url";\r
-\r
-               // add url to cache\r
-               $HOTPOT_RELATIVE_URLS[$relativeurl] = $url;\r
-       }\r
-       return $url;\r
-}\r
-\r
-// ===================================================\r
-// function for adding attempt questions and responses\r
-// ===================================================\r
-\r
-function hotpot_add_attempt_details(&$attempt) {\r
-\r
-       // encode ampersands so that HTML entities are preserved in the XML parser\r
-       // N.B. ampersands inside <![CDATA[ ]]> blocks do NOT need to be encoded\r
-\r
-       $old = &$attempt->details; // shortcut to "old" details\r
-       $new = '';\r
-       $str_start = 0;\r
-       while (($cdata_start = strpos($old, '<![CDATA[', $str_start)) && ($cdata_end = strpos($old, ']]>', $cdata_start))) {\r
-               $cdata_end += 3;\r
-               $new .= str_replace('&', '&amp;', substr($old, $str_start, $cdata_start-$str_start)).substr($old, $cdata_start, $cdata_end-$cdata_start);\r
-               $str_start = $cdata_end;\r
-       }\r
-       $new .= str_replace('&', '&amp;', substr($old, $str_start));\r
-       unset($old);\r
-\r
-       // parse the attempt details as xml\r
-       $details = new hotpot_xml_tree($new, "['hpjsresult']['#']");\r
-\r
-       $num = -1;\r
-       $q_num = -1;\r
-       $question = NULL;\r
-       $reponse = NULL;\r
-\r
-       $i = 0;\r
-       $tags = 'fields,field';\r
-\r
-       while (($field="[$i]['#']") && $details->xml_value($tags, $field)) {\r
-\r
-               $name = $details->xml_value($tags, $field."['fieldname'][0]['#']");\r
-               $data = $details->xml_value($tags, $field."['fielddata'][0]['#']");\r
-\r
-               // parse the field name into $matches\r
-               //      [1] quiz type\r
-               //      [2] attempt detail name\r
-               if (preg_match('/^(\w+?)_(\w+)$/', $name, $matches)) {\r
-                       $quiztype = strtolower($matches[1]);\r
-                       $name = strtolower($matches[2]);\r
-\r
-                       // parse the attempt detail $name into $matches\r
-                       //      [1] question number\r
-                       //      [2] question detail name\r
-                       if (preg_match('/^q(\d+)_(\w+)$/', $name, $matches)) {\r
-                               $num = $matches[1];\r
-                               $name = strtolower($matches[2]);\r
-                               $data = addslashes($data);\r
-\r
-                               // adjust JCross question numbers\r
-                               if (preg_match('/^(across|down)(.*)$/', $name, $matches)) {\r
-                                       $num .= '_'.$matches[1]; // e.g. 01_across, 02_down\r
-                                       $name = $matches[2];\r
-                                       if (substr($name, 0, 1)=='_') {\r
-                                               $name = substr($name, 1); // remove leading '_'\r
-                                       }\r
-                               }\r
-\r
-                               // is this a new question (or the first one)?\r
-                               if ($q_num<>$num) {\r
-\r
-                                       // add previous question and response, if any\r
-                                       hotpot_add_response($attempt, $question, $response);\r
-\r
-                                       // initialize question object\r
-                                       $question = NULL;\r
-                                       $question->name = '';\r
-                                       $question->text = '';\r
-                                       $question->hotpot = $attempt->hotpot;\r
-\r
-                                       // initialize response object\r
-                                       $response = NULL;\r
-                                       $response->attempt = $attempt->id;\r
-\r
-                                       // update question number\r
-                                       $q_num = $num;\r
-                               }\r
-\r
-                               // adjust field name and value, and set question type\r
-                               // (may not be necessary one day)\r
-                               hotpot_adjust_response_field($quiztype, $question, $num, $name, $data);\r
-\r
-                               // add $data to the question/response details\r
-                               switch ($name) {\r
-                                       case 'name':\r
-                                       case 'type':\r
-                                               $question->$name = $data;\r
-                                               break;\r
-                                       case 'text':\r
-                                               $question->$name = hotpot_string_id($data);\r
-                                               break;\r
-\r
-                                       case 'correct':\r
-                                       case 'ignored':\r
-                                       case 'wrong':\r
-                                               $response->$name = hotpot_string_ids($data);\r
-                                               break;\r
-\r
-                                       case 'score':\r
-                                       case 'weighting':\r
-                                       case 'hints':\r
-                                       case 'clues':\r
-                                       case 'checks':\r
-                                               $response->$name = intval($data);\r
-                                               break;\r
-                               }\r
-\r
-                       } else { // attempt details\r
-\r
-                               // adjust field name and value\r
-                               hotpot_adjust_response_field($quiztype, $question, $num='', $name, $data);\r
-\r
-                               // add $data to the attempt details\r
-                               if ($name=='penalties') {\r
-                                       $attempt->$name = intval($data);\r
-                               }\r
-                       }\r
-               }\r
-\r
-               $i++;\r
-       } // end while\r
-\r
-       // add the final question and response, if any\r
-       hotpot_add_response($attempt, $question, $response);\r
-}\r
-function hotpot_add_response(&$attempt, &$question, &$response) {\r
-       global $db, $next_url;\r
-\r
-       $loopcount = 1;\r
-\r
-       $looping = isset($question) && isset($question->name) && isset($response);\r
-       while ($looping) {\r
-\r
-               if ($loopcount==1) {\r
-                       $questionname = $question->name;\r
-               }\r
-\r
-               if (!$question->id = get_field('hotpot_questions', 'id', 'name', $question->name, 'hotpot', $attempt->hotpot)) {\r
-                       // add question record\r
-                       if (!$question->id = insert_record('hotpot_questions', $question)) {\r
-                               error("Could not add question record (attempt_id=$attempt->id): ".$db->ErrorMsg(), $next_url);\r
-                       }\r
-               }\r
-\r
-               if (record_exists('hotpot_responses', 'attempt', $attempt->id, 'question', $question->id)) {\r
-                       // there is already a response to this question for this attempt\r
-                       // probably because this quiz has two questions with the same text\r
-                       //      e.g. Which one of these answers is correct?\r
-\r
-                       // To workaround this, we create new question names\r
-                       //      e.g. Which one of these answers is correct? (2)\r
-                       // until we get a question name for which there is no response yet on this attempt\r
-\r
-                       $loopcount++;\r
-                       $question->name = "$questionname ($loopcount)";\r
-\r
-                       // This method fails to correctly identify questions in\r
-                       // quizzes which allow questions to be shuffled or omitted.\r
-                       // As yet, there is no workaround for such cases.\r
-\r
-               } else {\r
-                       $response->question = $question->id;\r
-\r
-                       // add response record\r
-                       if(!$response->id = insert_record('hotpot_responses', $response)) {\r
-                               error("Could not add response record (attempt_id=$attempt->id, question_id=$question->id): ".$db->ErrorMsg(), $next_url);\r
-                       }\r
-\r
-                       // we can stop looping now\r
-                       $looping = false;\r
-               }\r
-       } // end while\r
-}\r
-function hotpot_adjust_response_field($quiztype, &$question, &$num, &$name, &$data) {\r
-       switch ($quiztype) {\r
-               case 'jbc':\r
-                       $question->type = HOTPOT_JCB;\r
-                       switch ($name) {\r
-                               case 'right':\r
-                                       $name = 'correct';\r
-                               break;\r
-                       }\r
-                       break;\r
-               case 'jcloze':\r
-                       $question->type = HOTPOT_JCLOZE;\r
-                       if (is_numeric($num)) {\r
-                               $question->name = $num;\r
-                       }\r
-                       switch ($name) {\r
-                               case 'penalties':\r
-                                       if (is_numeric($num)) {\r
-                                               $name = 'checks';\r
-                                               if (is_numeric($data)) {\r
-                                                       $data++;\r
-                                               }\r
-                                       }\r
-                                       break;\r
-                               case 'clue_shown':\r
-                                       $name = 'clues';\r
-                                       $data = ($data=='YES' ? 1 : 0);\r
-                                       break;\r
-                               case 'clue_text':\r
-                                       $name = 'text';\r
-                                       break;\r
-                       }\r
-                       break;\r
-               case 'jcross':\r
-                       $question->type = HOTPOT_JCROSS;\r
-                       $question->name = $num;\r
-                       switch ($name) {\r
-                               case '': // HotPot v2.0.x\r
-                                       $name = 'correct';\r
-                                       break;\r
-                               case 'clue':\r
-                                       $name = 'text';\r
-                                       break;\r
-                       }\r
-                       break;\r
-               case 'jmatch':\r
-                       $question->type = HOTPOT_JMATCH;\r
-                       switch ($name) {\r
-                               case 'attempts':\r
-                                       $name = 'penalties';\r
-                                       if (is_numeric($data) && $data>0) {\r
-                                               $data--;\r
-                                       }\r
-                               break;\r
-                               case 'lhs':\r
-                                       $name = 'name';\r
-                               break;\r
-                               case 'rhs':\r
-                                       $name = 'correct';\r
-                               break;\r
-                       }\r
-                       break;\r
-               case 'jmix':\r
-                       $question->type = HOTPOT_JMIX;\r
-                       $question->name = $num;\r
-                       switch ($name) {\r
-                               // keep these in for "restore" of courses\r
-                               // which were backed up with HotPot v2.0.x\r
-                               case 'wrongguesses':\r
-                                       $name = 'checks';\r
-                                       if (is_numeric($data)) {\r
-                                               $data++;\r
-                                       }\r
-                               break;\r
-                               case 'right':\r
-                                       $name = 'correct';\r
-                               break;\r
-                       }\r
-                       break;\r
-                       break;\r
-               case 'jquiz':\r
-                       switch ($name) {\r
-                               case 'type':\r
-                                       $data = HOTPOT_JQUIZ;\r
-                                       switch ($data) {\r
-                                               case 'multiple-choice':\r
-                                                       $data .= '.'.HOTPOT_JQUIZ_MULTICHOICE;\r
-                                               break;\r
-                                               case 'short-answer':\r
-                                                       $data .= '.'.HOTPOT_JQUIZ_SHORTANSWER;\r
-                                               break;\r
-                                               case 'hybrid':\r
-                                                       $data .= '.'.HOTPOT_JQUIZ_HYBRID;\r
-                                               break;\r
-                                               case 'multi-select':\r
-                                                       $data .= '.'.HOTPOT_JQUIZ_MULTISELECT;\r
-                                               case 'n/a':\r
-                                               default:\r
-                                                       // do nothing more\r
-                                               break;\r
-                                       }\r
-                               break;\r
-                               case 'question':\r
-                                       $name = 'name';\r
-                               break;\r
-                       }\r
-                       break;\r
-\r
-               case 'rhubarb':\r
-                       $question->type = HOTPOT_TEXTOYS_RHUBARB;\r
-                       if (empty($question->name)) {\r
-                               $question->name = $num;\r
-                       }\r
-                       break;\r
-\r
-               case 'sequitur':\r
-                       $question->type = HOTPOT_TEXTOYS_SEQUITUR;\r
-                       break;\r
-       }\r
-}\r
-function hotpot_string_ids($field_value) {\r
-       $ids = array();\r
-       $strings = explode(',', $field_value);\r
-       foreach($strings as $str) {\r
-               if ($id = hotpot_string_id($str)) {\r
-                       $ids[] = $id;\r
-               }\r
-       }\r
-       return implode(',', $ids);\r
-}\r
-function hotpot_string_id($str) {\r
-       $id = '';\r
-       if (isset($str) && $str<>'') {\r
-\r
-               // get the id from the table if it is already there\r
-               if (!$id = get_field('hotpot_strings', 'id', 'string', $str)) {\r
-\r
-                       // create a string record\r
-                       $record = NULL;\r
-                       $record->string = $str;\r
-\r
-                       // try and add the new string record\r
-                       if (!$id = insert_record('hotpot_strings', $record)) {\r
-                               global $db;\r
-                               error("Could not add string record for '".htmlspecialchars($str)."': ".$db->ErrorMsg());\r
-                       }\r
-               }\r
-       }\r
-       return $id;\r
-}\r
-\r
-if (!function_exists('file_get_contents')) {\r
-       // add this function for php version<4.3\r
-       function file_get_contents($filepath) {\r
-               $contents = file($filepath);\r
-               if (is_array($contents)) {\r
-                        $contents = implode('', $contents);\r
-               }\r
-               return $contents;\r
-       }\r
-}\r
-if (!function_exists('html_entity_decode')) {\r
-       // add this function for php version<4.3\r
-       function html_entity_decode($str) {\r
-               $t = get_html_translation_table(HTML_ENTITIES);\r
-               $t = array_flip($t);\r
-               return strtr($str, $t);\r
-       }\r
-\r
-}\r
-\r
-// required for Moodle 1.x\r
-if (!isset($CFG->pixpath)) {\r
-       $CFG->pixpath = "$CFG->wwwroot/pix";\r
-}\r
-\r
-if (!function_exists('fullname')) {\r
-       // add this function for Moodle 1.x\r
-       function fullname($user) {\r
-               return "$user->firstname $user->lastname";\r
-       }\r
-}\r
-if (!function_exists('get_user_preferences')) {\r
-       // add this function for Moodle 1.x\r
-       function get_user_preferences($name=NULL, $default=NULL, $userid=NULL) {\r
-               return $default;\r
-       }\r
-}\r
-if (!function_exists('set_user_preference')) {\r
-       // add this function for Moodle 1.x\r
-       function set_user_preference($name, $value, $otheruser=NULL) {\r
-               return false;\r
-       }\r
-}\r
-function hotpot_utf8_to_html_entity($char) {\r
-       // http://www.zend.com/codex.php?id=835&single=1\r
-\r
-       // array used to figure what number to decrement from character order value\r
-       // according to number of characters used to map unicode to ascii by utf-8\r
-       static $HOTPOT_UTF8_DECREMENT = array(\r
-               1=>0, 2=>192, 3=>224, 4=>240\r
-       );\r
-\r
-       // the number of bits to shift each character by\r
-       static $HOTPOT_UTF8_SHIFT = array(\r
-               1=>array(0=>0),\r
-               2=>array(0=>6,  1=>0),\r
-               3=>array(0=>12, 1=>6,  2=>0),\r
-               4=>array(0=>18, 1=>12, 2=>6, 3=>0)\r
-       );\r
-\r
-       $dec = 0;\r
-       $len = strlen($char);\r
-       for ($pos=0; $pos<$len; $pos++) {\r
-               $ord = ord ($char{$pos});\r
-               $ord -= ($pos ? 128 : $HOTPOT_UTF8_DECREMENT[$len]);\r
-               $dec += ($ord << $HOTPOT_UTF8_SHIFT[$len][$pos]);\r
-       }\r
-       return '&#x'.sprintf('%04X', $dec).';';\r
-}\r
-\r
-function hotpot_print_show_links($course, $location, $reference, $actions='', $spacer=' &nbsp; ', $new_window=false) {\r
-       global $CFG;\r
-       if (is_string($actions)) {\r
-               if (empty($actions)) {\r
-                       $actions = 'showxmlsource,showxmltree,showhtmlsource';\r
-               }\r
-               $actions = explode(',', $actions);\r
-       }\r
-       $strenterafilename = get_string('enterafilename', 'hotpot');\r
-       $html = <<<END_OF_SCRIPT\r
-<script type="text/javascript" language="javascript">\r
-<!--\r
-       function setLink(lnk) {\r
-               var form = document.forms['form'];\r
-               return setLinkAttribute(lnk, 'reference', form) && setLinkAttribute(lnk, 'location', form);\r
-       }\r
-       function setLinkAttribute(lnk, name, form) {\r
-               // set link attribute value using\r
-               // f(orm) name and e(lement) name\r
-\r
-               var r = true; // result\r
-\r
-               var obj = (form) ? form.elements[name] : null;\r
-               if (obj) {\r
-                       r = false;\r
-                       var v = getObjValue(obj);\r
-                       if (v=='') {\r
-                               alert('$strenterafilename');\r
-                       } else {\r
-                               var s = lnk.href;\r
-                               var i = s.indexOf('?');\r
-                               if (i>=0) {\r
-                                       i = s.indexOf(name+'=', i+1);\r
-                                       if (i>=0) {\r
-                                               i += name.length+1;\r
-                                               var ii = s.indexOf('&', i);\r
-                                               if (ii<0) {\r
-                                                       ii = s.length;\r
-                                               }\r
-                                               lnk.href = s.substring(0, i) + v + s.substring(ii);\r
-                                               r = true;\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               return r;\r
-       }\r
-       function getObjValue(obj) {\r
-               var v = ''; // the value\r
-               var t = (obj && obj.type) ? obj.type : "";\r
-               if (t=="text" || t=="textarea" || t=="hidden") {\r
-                       v = obj.value;\r
-               } else if (t=="select-one" || t=="select-multiple") {\r
-                       var l = obj.options.length;\r
-                       for (var i=0; i<l; i++) {\r
-                               if (obj.options[i].selected) {\r
-                                       v += (v=="" ? "" : ",") + obj.options[i].value;\r
-                               }\r
-                       }\r
-               }\r
-               return v;\r
-       }\r
-       function getDir(s) {\r
-               if (s.charAt(0)!='/') {\r
-                       s = '/' + s;\r
-               }\r
-               var i = s.lastIndexOf('/');\r
-               return s.substring(0, i);\r
-       }\r
-//-->\r
-</script>\r
-END_OF_SCRIPT;\r
-\r
-       foreach ($actions as $action) {\r
-               $html .= $spacer\r
-               .       '<a href="'\r
-               .                       $CFG->wwwroot.'/mod/hotpot/show.php'\r
-               .                       '?course='.$course.'&location='.$location.'&reference='.urlencode($reference).'&action='.$action\r
-               .               '"'\r
-               .               ' onclick="return setLink(this);"'\r
-               .               ($new_window ? ' target="_blank"' : '')\r
-               .       '>'.get_string($action, 'hotpot').'</a>'\r
-               ;\r
-       }\r
-       print '<span class="helplink">'.$html.'</span>';\r
-}\r
-\r
-?>\r
+<?PHP  // $Id$
+
+//////////////////////////////////
+/// CONFIGURATION settings
+
+if (!isset($CFG->hotpot_showtimes)) {
+       set_config("hotpot_showtimes", 0);
+}
+if (!isset($CFG->hotpot_excelencodings)) {
+       set_config("hotpot_excelencodings", "");
+}
+
+//////////////////////////////////
+/// CONSTANTS and GLOBAL VARIABLES
+
+$CFG->hotpotroot = "$CFG->dirroot/mod/hotpot";
+$CFG->hotpottemplate = "$CFG->hotpotroot/template";
+$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']);
+
+define("HOTPOT_JS", "$CFG->wwwroot/mod/hotpot/hotpot-full.js");
+
+define("HOTPOT_NO",  "0");
+define("HOTPOT_YES", "1");
+
+define ("HOTPOT_TEXTSOURCE_QUIZ", "0");
+define ("HOTPOT_TEXTSOURCE_FILENAME", "1");
+define ("HOTPOT_TEXTSOURCE_FILEPATH", "2");
+define ("HOTPOT_TEXTSOURCE_SPECIFIC", "3");
+
+define("HOTPOT_LOCATION_COURSEFILES", "0");
+define("HOTPOT_LOCATION_SITEFILES",   "1");
+
+$HOTPOT_LOCATION = array (
+       HOTPOT_LOCATION_COURSEFILES => get_string("coursefiles"),
+       HOTPOT_LOCATION_SITEFILES   => get_string("sitefiles"),
+);
+
+define("HOTPOT_OUTPUTFORMAT_BEST",     "1");
+define("HOTPOT_OUTPUTFORMAT_V3",      "10");
+define("HOTPOT_OUTPUTFORMAT_V4",      "11");
+define("HOTPOT_OUTPUTFORMAT_V5",      "12");
+define("HOTPOT_OUTPUTFORMAT_V5_PLUS", "13");
+define("HOTPOT_OUTPUTFORMAT_V6",      "14");
+define("HOTPOT_OUTPUTFORMAT_V6_PLUS", "15");
+define("HOTPOT_OUTPUTFORMAT_FLASH",   "20");
+define("HOTPOT_OUTPUTFORMAT_MOBILE",  "30");
+
+$HOTPOT_OUTPUTFORMAT = array (
+       HOTPOT_OUTPUTFORMAT_BEST    => get_string("outputformat_best", "hotpot"),
+       HOTPOT_OUTPUTFORMAT_V6_PLUS => get_string("outputformat_v6_plus", "hotpot"),
+       HOTPOT_OUTPUTFORMAT_V6      => get_string("outputformat_v6", "hotpot"),
+       HOTPOT_OUTPUTFORMAT_V5_PLUS => get_string("outputformat_v5_plus", "hotpot"),
+       HOTPOT_OUTPUTFORMAT_V5      => get_string("outputformat_v5", "hotpot"),
+       HOTPOT_OUTPUTFORMAT_V4      => get_string("outputformat_v4", "hotpot"),
+       HOTPOT_OUTPUTFORMAT_V3      => get_string("outputformat_v3", "hotpot"),
+       HOTPOT_OUTPUTFORMAT_FLASH   => get_string("outputformat_flash", "hotpot"),
+       HOTPOT_OUTPUTFORMAT_MOBILE  => get_string("outputformat_mobile", "hotpot"),
+);
+$HOTPOT_OUTPUTFORMAT_DIR = array (
+       HOTPOT_OUTPUTFORMAT_V6_PLUS => 'v6',
+       HOTPOT_OUTPUTFORMAT_V6      => 'v6',
+       HOTPOT_OUTPUTFORMAT_V5_PLUS => 'v5',
+       HOTPOT_OUTPUTFORMAT_V5      => 'v5',
+       HOTPOT_OUTPUTFORMAT_V4      => 'v4',
+       HOTPOT_OUTPUTFORMAT_V3      => 'v3',
+       HOTPOT_OUTPUTFORMAT_FLASH   => 'flash',
+       HOTPOT_OUTPUTFORMAT_MOBILE  => 'mobile',
+);
+foreach ($HOTPOT_OUTPUTFORMAT_DIR as $format=>$dir) {
+       if (is_file("$CFG->hotpottemplate/$dir.php") && is_dir("$CFG->hotpottemplate/$dir")) {
+               // do nothing ($format is available)
+       } else {
+               // $format is not available, so remove it
+               unset($HOTPOT_OUTPUTFORMAT[$format]);
+               unset($HOTPOT_OUTPUTFORMAT_DIR[$format]);
+       }
+}
+define("HOTPOT_NAVIGATION_BAR",     "1");
+define("HOTPOT_NAVIGATION_FRAME",   "2");
+define("HOTPOT_NAVIGATION_IFRAME",  "3");
+define("HOTPOT_NAVIGATION_BUTTONS", "4");
+define("HOTPOT_NAVIGATION_GIVEUP",  "5");
+define("HOTPOT_NAVIGATION_NONE",    "6");
+
+$HOTPOT_NAVIGATION = array (
+       HOTPOT_NAVIGATION_BAR     => get_string("navigation_bar", "hotpot"),
+       HOTPOT_NAVIGATION_FRAME   => get_string("navigation_frame", "hotpot"),
+       HOTPOT_NAVIGATION_IFRAME  => get_string("navigation_iframe", "hotpot"),
+       HOTPOT_NAVIGATION_BUTTONS => get_string("navigation_buttons", "hotpot"),
+       HOTPOT_NAVIGATION_GIVEUP  => get_string("navigation_give_up", "hotpot"),
+       HOTPOT_NAVIGATION_NONE    => get_string("navigation_none", "hotpot"),
+);
+
+define("HOTPOT_JCB",    "1");
+define("HOTPOT_JCLOZE", "2");
+define("HOTPOT_JCROSS", "3");
+define("HOTPOT_JMATCH", "4");
+define("HOTPOT_JMIX",   "5");
+define("HOTPOT_JQUIZ",  "6");
+define("HOTPOT_TEXTOYS_RHUBARB",   "7");
+define("HOTPOT_TEXTOYS_SEQUITUR",  "8");
+
+$HOTPOT_QUIZTYPE = array(
+       HOTPOT_JCB    => 'JCB',
+       HOTPOT_JCLOZE => 'JCloze',
+       HOTPOT_JCROSS => 'JCross',
+       HOTPOT_JMATCH => 'JMatch',
+       HOTPOT_JMIX   => 'JMix',
+       HOTPOT_JQUIZ  => 'JQuiz',
+       HOTPOT_TEXTOYS_RHUBARB  => 'Rhubarb',
+       HOTPOT_TEXTOYS_SEQUITUR => 'Sequitur'
+);
+
+define("HOTPOT_JQUIZ_MULTICHOICE", "1");
+define("HOTPOT_JQUIZ_SHORTANSWER", "2");
+define("HOTPOT_JQUIZ_HYBRID",      "3");
+define("HOTPOT_JQUIZ_MULTISELECT", "4");
+
+define("HOTPOT_GRADEMETHOD_HIGHEST", "1");
+define("HOTPOT_GRADEMETHOD_AVERAGE", "2");
+define("HOTPOT_GRADEMETHOD_FIRST",   "3");
+define("HOTPOT_GRADEMETHOD_LAST",    "4");
+
+$HOTPOT_GRADEMETHOD = array (
+       HOTPOT_GRADEMETHOD_HIGHEST => get_string("gradehighest", "quiz"),
+       HOTPOT_GRADEMETHOD_AVERAGE => get_string("gradeaverage", "quiz"),
+       HOTPOT_GRADEMETHOD_FIRST   => get_string("attemptfirst", "quiz"),
+       HOTPOT_GRADEMETHOD_LAST    => get_string("attemptlast",  "quiz"),
+);
+
+define("HOTPOT_STATUS_INPROGRESS", "1");
+define("HOTPOT_STATUS_TIMEDOUT",   "2");
+define("HOTPOT_STATUS_ABANDONED",  "3");
+define("HOTPOT_STATUS_COMPLETED",  "4");
+
+$HOTPOT_STATUS = array (
+       HOTPOT_STATUS_INPROGRESS => get_string("inprogress", "hotpot"),
+       HOTPOT_STATUS_TIMEDOUT   => get_string("timedout",   "hotpot"),
+       HOTPOT_STATUS_ABANDONED  => get_string("abandoned",  "hotpot"),
+       HOTPOT_STATUS_COMPLETED  => get_string("completed",  "hotpot"),
+);
+
+define("HOTPOT_FEEDBACK_NONE", "0");
+define("HOTPOT_FEEDBACK_WEBPAGE", "1");
+define("HOTPOT_FEEDBACK_FORMMAIL", "2");
+define("HOTPOT_FEEDBACK_MOODLEFORUM", "3");
+define("HOTPOT_FEEDBACK_MOODLEMESSAGING", "4");
+
+$HOTPOT_FEEDBACK = array (
+       HOTPOT_FEEDBACK_NONE => get_string("feedbacknone", "hotpot"),
+       HOTPOT_FEEDBACK_WEBPAGE => get_string("feedbackwebpage",  "hotpot"),
+       HOTPOT_FEEDBACK_FORMMAIL => get_string("feedbackformmail", "hotpot"),
+       HOTPOT_FEEDBACK_MOODLEFORUM => get_string("feedbackmoodleforum", "hotpot"),
+       HOTPOT_FEEDBACK_MOODLEMESSAGING => get_string("feedbackmoodlemessaging", "hotpot"),
+);
+if (empty($CFG->messaging)) { // Moodle 1.4 (and less)
+       unset($HOTPOT_FEEDBACK[HOTPOT_FEEDBACK_MOODLEMESSAGING]);
+}
+
+define("HOTPOT_DISPLAYNEXT_QUIZ",   "0");
+define("HOTPOT_DISPLAYNEXT_COURSE", "1");
+define("HOTPOT_DISPLAYNEXT_INDEX",  "2");
+
+//////////////////////////////////
+/// CORE FUNCTIONS
+
+
+// possible return values:
+//    false:
+//        display moderr.html (if exists) OR "Could not update" and return to couse view
+//    string:
+//        display as error message and return to course view
+//  true (or non-zero number):
+//        continue to $hp->redirect (if set) OR hotpot/view.php (to displsay quiz)
+
+// $hp is an object containing the values of the form in mod.html
+// i.e. all the fields in the 'hotpot' table, plus the following:
+//     $hp->course       : an id in the 'course' table
+//     $hp->coursemodule : an id in the 'course_modules' table
+//     $hp->section      : an id in the 'course_sections' table
+//     $hp->module       : an id in the 'modules' table
+//     $hp->modulename   : always 'hotpot'
+//     $hp->instance     : an id in the 'hotpot' table
+//     $hp->mode         : 'add' or 'update'
+//     $hp->sesskey      : unique string required for Moodle's session management
+
+function hotpot_add_instance(&$hp) {
+       if (hotpot_set_form_values($hp)) {
+               $result = insert_record("hotpot", $hp);
+       } else {
+               $result=  false;
+       }
+       return $result;
+}
+
+function hotpot_update_instance(&$hp) {
+       if (hotpot_set_form_values($hp)) {
+               $hp->id = $hp->instance;
+               $result = update_record("hotpot", $hp);
+       } else {
+               $result=  false;
+       }
+       return $result;
+}
+
+function hotpot_set_form_values(&$hp) {
+       $ok = true;
+       $hp->errors = array(); // these will be reported by moderr.html
+
+       if (empty($hp->reference)) {
+               $ok = false;
+               $hp->errors['reference']= get_string('error_nofilename', 'hotpot');
+       }
+
+       if ($hp->studentfeedbackurl=='http://') {
+               $hp->studentfeedbackurl = '';
+       }
+
+       if (empty($hp->studentfeedbackurl)) {
+               switch ($hp->studentfeedback) {
+                       case HOTPOT_FEEDBACK_WEBPAGE:
+                               $ok = false;
+                               $hp->errors['studentfeedbackurl']= get_string('error_nofeedbackurlwebpage', 'hotpot');
+                       break;
+                       case HOTPOT_FEEDBACK_FORMMAIL:
+                               $ok = false;
+                               $hp->errors['studentfeedbackurl']= get_string('error_nofeedbackurlformmail', 'hotpot');
+                       break;
+               }
+       }
+
+       $time = time();
+       $hp->timecreated = $time;
+       $hp->timemodified = $time;
+
+       if (empty($hp->enabletimeopen)) {
+               $hp->timeopen = 0;
+       } else {
+               $hp->timeopen = make_timestamp(
+                       $hp->openyear, $hp->openmonth, $hp->openday,
+                       $hp->openhour, $hp->openminute, 0
+               );
+       }
+
+       if (empty($hp->enabletimeclose)) {
+               $hp->timeclose = 0;
+       } else {
+               $hp->timeclose = make_timestamp(
+                       $hp->closeyear, $hp->closemonth, $hp->closeday,
+                       $hp->closehour, $hp->closeminute, 0
+               );
+       }
+
+       if ($hp->quizchain==HOTPOT_YES) {
+               switch ($hp->mode) {
+                       case 'add':
+                               $ok = hotpot_add_chain($hp);
+                       break;
+                       case 'update':
+                               $ok = hotpot_update_chain($hp);
+                       break;
+               }
+       } else { // $hp->quizchain==HOTPOT_NO
+               hotpot_set_name_summary_reference($hp);
+       }
+
+       switch ($hp->displaynext) {
+               // N.B. redirection only works for Moodle 1.5+
+               case HOTPOT_DISPLAYNEXT_COURSE:
+                       $hp->redirect = true;
+                       $hp->redirecturl = "view.php?id=$hp->course";
+                       break;
+               case HOTPOT_DISPLAYNEXT_INDEX:
+                       $hp->redirect = true;
+                       $hp->redirecturl = "../mod/hotpot/index.php?id=$hp->course";
+                       break;
+               default:
+                       // use Moodle default action (i.e. go on to display the hotpot quiz)
+       }
+
+       // if ($ok && $hp->setdefaults) {
+       if ($ok) {
+               set_user_preference('hotpot_timeopen', $hp->timeopen);
+               set_user_preference('hotpot_timeclose', $hp->timeclose);
+               set_user_preference('hotpot_navigation', $hp->navigation);
+               set_user_preference('hotpot_outputformat', $hp->outputformat);
+               set_user_preference('hotpot_studentfeedback', $hp->studentfeedback);
+               set_user_preference('hotpot_studentfeedbackurl', $hp->studentfeedbackurl);
+               set_user_preference('hotpot_forceplugins', $hp->forceplugins);
+               set_user_preference('hotpot_shownextquiz', $hp->shownextquiz);
+               set_user_preference('hotpot_review', $hp->review);
+               set_user_preference('hotpot_grade', $hp->grade);
+               set_user_preference('hotpot_grademethod', $hp->grademethod);
+               set_user_preference('hotpot_attempts', $hp->attempts);
+               set_user_preference('hotpot_subnet', $hp->subnet);
+               set_user_preference('hotpot_displaynext', $hp->displaynext);
+               if ($hp->mode=='add') {
+                       set_user_preference('hotpot_quizchain', $hp->quizchain);
+                       set_user_preference('hotpot_namesource', $hp->namesource);
+                       set_user_preference('hotpot_summarysource', $hp->summarysource);
+               }
+       }
+       return $ok;
+}
+function hotpot_get_chain(&$cm) {
+       // get details of course_modules in this section
+       $course_module_ids = get_field('course_sections', 'sequence', 'id', $cm->section);
+       if (empty($course_module_ids)) {
+               $hotpot_modules = array();
+       } else {
+               $hotpot_modules = get_records_select('course_modules', "id IN ($course_module_ids) AND module=$cm->module");
+               if (empty($hotpot_modules)) {
+                       $hotpot_modules = array();
+               }
+       }
+
+       // get ids of hotpot modules in this section
+       $ids = array();
+       foreach ($hotpot_modules as $hotpot_module) {
+               $ids[] = $hotpot_module->instance;
+       }
+
+       // get details of hotpots in this section
+       if (empty($ids)) {
+               $hotpots = array();
+       } else {
+               $hotpots = get_records_list('hotpot', 'id', implode(',', $ids));
+       }
+
+       $found = false;
+       $chain = array();
+
+       // loop through course_modules in this section
+       $ids = explode(',', $course_module_ids);
+       foreach ($ids as $id) {
+
+               // check this course_module is a hotpot activity
+               if (isset($hotpot_modules[$id])) {
+
+                       // store details of this course module and hotpot activity
+                       $hotpot_id = $hotpot_modules[$id]->instance;
+                       $chain[$id] = &$hotpot_modules[$id];
+                       $chain[$id]->hotpot = &$hotpots[$hotpot_id];
+
+                       // set $found, if this is the course module we're looking for
+                       if (isset($cm->coursemodule)) {
+                               if ($id==$cm->coursemodule) {
+                                       $found = true;
+                               }
+                       } else {
+                               if ($id==$cm->id) {
+                                       $found = true;
+                               }
+                       }
+
+                       // is this the end of a chain
+                       if (empty($hotpots[$hotpot_id]->shownextquiz)) {
+                               if ($found) {
+                                       break; // out of loop
+                               } else {
+                                       // restart chain (target cm has not been found yet)
+                                       $chain = array();
+                               }
+                       }
+               }
+       } // end foreach $ids
+
+       return $found ? $chain : false;
+}
+function hotpot_is_visible(&$cm) {
+       if (!isset($cm->sectionvisible)) {
+               if ($section = get_record('course_sections', 'id', $cm->section)) {
+                       $cm->sectionvisible = $section->visible;
+               } else {
+                       error('Course module record contains invalid section');
+               }
+       }
+
+       if (empty($cm->sectionvisible)) {
+               $visible = HOTPOT_NO;
+       } else {
+               $visible = HOTPOT_YES;
+               if (empty($cm->visible)) {
+                       if ($chain = hotpot_get_chain($cm)) {
+                               $startofchain = array_shift($chain);
+                               $visible = $startofchain->visible;
+                       }
+               }
+       }
+       return $visible;
+}
+function hotpot_add_chain(&$hp) {
+/// add a chain of hotpot actiivities
+
+       global $CFG, $course;
+
+       $ok = true;
+       $hp->names = array();
+       $hp->summaries = array();
+       $hp->references = array();
+
+       $xml_quiz = new hotpot_xml_quiz($hp, false, false, false, false, false);
+
+       if (isset($xml_quiz->error)) {
+               $hp->errors['reference'] = $xml_quiz->error;
+               $ok = false;
+
+       } else if (is_dir($xml_quiz->filepath)) {
+
+               // get list of hotpot files in this folder
+               if ($dh = @opendir($xml_quiz->filepath)) {
+                       while ($file = @readdir($dh)) {
+                               if (preg_match('/\.(jbc|jcl|jcw|jmt|jmx|jqz|htm|html)$/', $file)) {
+                                       $hp->references[] = "$xml_quiz->reference/$file";
+                               }
+                       }
+                       closedir($dh);
+
+                       // get titles
+                       foreach ($hp->references as $i=>$reference) {
+                               $filepath = $xml_quiz->fileroot.'/'.$reference;
+                               hotpot_get_titles_and_next_ex($hp, $filepath);
+                               $hp->names[$i] = $hp->exercisetitle;
+                               $hp->summaries[$i] = $hp->exercisesubtitle;
+                       }
+
+               } else {
+                       $ok = false;
+                       $hp->errors['reference'] = get_string('error_couldnotopenfolder', 'hotpot', $hp->reference);
+               }
+
+       } else if (is_file($xml_quiz->filepath)) {
+
+               $filerootlength = strlen($xml_quiz->fileroot) + 1;
+
+               while ($xml_quiz->filepath) {
+                       hotpot_get_titles_and_next_ex($hp, $xml_quiz->filepath, true);
+                       $hp->names[] = $hp->exercisetitle;
+                       $hp->summaries[] = $hp->exercisesubtitle;
+                       $hp->references[] = substr($xml_quiz->filepath, $filerootlength);
+
+                       if ($hp->nextexercise) {
+                               $filepath = $xml_quiz->fileroot.'/'.$xml_quiz->filesubdir.$hp->nextexercise;
+
+                               // check file is not already in chain
+                               $reference = substr($filepath, $filerootlength);
+                               if (in_array($reference, $hp->references)) {
+                                       $filepath = '';
+                               }
+                       } else {
+                               $filepath = '';
+                       }
+                       if ($filepath && file_exists($filepath) && is_file($filepath) && is_readable($filepath)) {
+                               $xml_quiz->filepath = $filepath;
+                       } else {
+                               $xml_quiz->filepath = false; // finish while loop
+                       }
+               } // end while
+
+       } else {
+               $ok = false;
+               $hp->errors['reference'] = get_string('error_notfileorfolder', 'hotpot', $hp->reference);
+       }
+
+       if (empty($hp->references) && empty($hp->errors['reference'])) {
+               $ok = false;
+               $hp->errors['reference'] = get_string('error_noquizzesfound', 'hotpot', $hp->reference);
+       }
+
+       if ($ok) {
+               $hp->visible = HOTPOT_YES;
+
+               if (trim($hp->name)=='') {
+                       $hp->name = get_string("modulename", $hp->modulename);
+               }
+               $hp->specificname = $hp->name;
+               $hp->specificsummary = $hp->summary;
+
+               // add all except last activity in chain
+
+               $i_max = count($hp->references)-1;
+               for ($i=0; $i<$i_max; $i++) {
+
+                       hotpot_set_name_summary_reference($hp, $i);
+                       $hp->reference = addslashes($hp->reference);
+
+                       if (!$hp->instance = insert_record("hotpot", $hp)) {
+                               error("Could not add a new instance of $hp->modulename", "view.php?id=$hp->course");
+                       }
+
+                       // store (hotpot table) id of start of chain
+                       if ($i==0) {
+                               $hp->startofchain = $hp->instance;
+                       }
+
+                       if (isset($course->groupmode)) {
+                               $hp->groupmode = $course->groupmode;
+                       }
+
+                       if (! $hp->coursemodule = add_course_module($hp)) {
+                               error("Could not add a new course module");
+                       }
+                       if (! $sectionid = add_mod_to_section($hp) ) {
+                               error("Could not add the new course module to that section");
+                       }
+
+                       if (! set_field("course_modules", "section", $sectionid, "id", $hp->coursemodule)) {
+                               error("Could not update the course module with the correct section");
+                       }
+
+                       add_to_log($hp->course, "course", "add mod",
+                               "../mod/$hp->modulename/view.php?id=$hp->coursemodule",
+                               "$hp->modulename $hp->instance"
+                       );
+                       add_to_log($hp->course, $hp->modulename, "add",
+                               "view.php?id=$hp->coursemodule",
+                               "$hp->instance", $hp->coursemodule
+                       );
+
+                       // hide tail of chain
+                       if ($hp->shownextquiz==HOTPOT_YES) {
+                               $hp->visible = HOTPOT_NO;
+                       }
+               } // end for ($hp->references)
+
+               // settings for final activity in chain
+               hotpot_set_name_summary_reference($hp, $i);
+               $hp->reference = addslashes($hp->references[$i]);
+               $hp->shownextquiz = HOTPOT_NO;
+
+               if (isset($hp->startofchain)) {
+                       // redirection only works for Moodle 1.5+
+                       $hp->redirect = true;
+                       $hp->redirecturl = "$CFG->wwwroot/mod/hotpot/view.php?hp=$hp->startofchain";
+               }
+       } // end if $ok
+
+       return $ok;
+}
+function hotpot_set_name_summary_reference(&$hp, $chain_index=NULL) {
+
+       $xml_quiz = NULL;
+
+       $textfields = array('name', 'summary');
+       foreach ($textfields as $textfield) {
+
+               $textsource = $textfield.'source';
+
+               // are we adding a chain?
+               if (isset($chain_index)) {
+
+                       switch ($hp->$textsource) {
+                               case HOTPOT_TEXTSOURCE_QUIZ:
+                                       if ($textfield=='name') {
+                                               $hp->exercisetitle = $hp->names[$chain_index];
+                                       } else if ($textfield=='summary') {
+                                               $hp->exercisesubtitle = $hp->summaries[$chain_index];
+                                       }
+                                       break;
+                               case HOTPOT_TEXTSOURCE_SPECIFIC:
+                                       $specifictext = 'specific'.$textfield;
+                                       if (empty($hp->$specifictext) && trim($hp->$specifictext)=='') {
+                                               $hp->$textfield = '';
+                                       } else {
+                                               $hp->$textfield = $hp->$specifictext.' ('.($chain_index+1).')';
+                                       }
+                                       break;
+                       }
+                       $hp->reference = $hp->references[$chain_index];
+               }
+
+               if ($hp->$textsource==HOTPOT_TEXTSOURCE_QUIZ) {
+                       if (empty($xml_quiz) && !isset($chain_index)) {
+                               $xml_quiz = new hotpot_xml_quiz($hp, false, false, false, false, false);
+                               hotpot_get_titles_and_next_ex($hp, $xml_quiz->filepath);
+                       }
+                       if ($textfield=='name') {
+                               $hp->$textfield = addslashes($hp->exercisetitle);
+                       } else if ($textfield=='summary') {
+                               $hp->$textfield = addslashes($hp->exercisesubtitle);
+                       }
+               }
+               switch ($hp->$textsource) {
+                       case HOTPOT_TEXTSOURCE_FILENAME:
+                               $hp->$textfield = basename($hp->reference);
+                               break;
+                       case HOTPOT_TEXTSOURCE_FILEPATH:
+                               $hp->$textfield = '';
+                               // continue to next lines
+                       default:
+                               if (empty($hp->$textfield)) {
+                                       $hp->$textfield = str_replace('/', ' ', $hp->reference);
+                               }
+               } // end switch
+       } // end foreach
+}
+function hotpot_get_titles_and_next_ex(&$hp, $filepath, $get_next=false) {
+
+       $hp->exercisetitle = '';
+       $hp->exercisesubtitle = '';
+       $hp->nextexercise = '';
+
+       // read the quiz file source
+       if ($source = file_get_contents($filepath)) {
+
+               $next = '';
+               $title = '';
+               $subtitle = '';
+
+               if (preg_match('|\.html?$|', $filepath)) {
+                       // html file
+                       if (preg_match('|<h2[^>]*class="ExerciseTitle"[^>]*>(.*?)</h2>|is', $source, $matches)) {
+                               $title = trim(strip_tags($matches[1]));
+                       }
+                       if (empty($title)) {
+                               if (preg_match('|<title[^>]*>(.*?)</title>|is', $source, $matches)) {
+                                       $title = trim(strip_tags($matches[1]));
+                               }
+                       }
+                       if (preg_match('|<h3[^>]*class="ExerciseSubtitle"[^>]*>(.*?)</h3>|is', $source, $matches)) {
+                               $subtitle = trim(strip_tags($matches[1]));
+                       }
+                       if ($get_next) {
+                               if (preg_match('|<div[^>]*class="NavButtonBar"[^>]*>(.*?)</div>|is', $source, $matches)) {
+                                       $navbuttonbar = $matches[1];
+                                       if (preg_match_all('|<button[^>]*class="NavButton"[^>]*onclick="'."location='([^']*)'".'[^"]*"[^>]*>|is', $navbuttonbar, $matches)) {
+                                               $lastbutton = count($matches[0])-1;
+                                               $next = $matches[1][$lastbutton];
+                                       }
+                               }
+                       }
+
+               } else {
+                       // xml file (...maybe)
+                       $xml_tree = new hotpot_xml_tree($source);
+                       $xml_tree->filetype = '';
+
+                       $keys = array_keys($xml_tree->xml);
+                       foreach ($keys as $key) {
+                               if (preg_match('/^(hotpot|textoys)-(\w+)-file$/i', $key, $matches)) {
+                                       $xml_tree->filetype = 'xml';
+                                       $xml_tree->xml_root = "['$key']['#']";
+                                       $xml_tree->quiztype = strtolower($matches[2]);
+                                       break;
+                               }
+                       }
+                       if ($xml_tree->filetype=='xml') {
+
+                               $title = strip_tags($xml_tree->xml_value('data,title'));
+                               $subtitle = $xml_tree->xml_value('hotpot-config-file,'.$xml_tree->quiztype.',exercise-subtitle');
+
+                               if ($get_next) {
+                                       $include = $xml_tree->xml_value('hotpot-config-file,global,include-next-ex');
+                                       if (!empty($include)) {
+                                               $next = $xml_tree->xml_value("hotpot-config-file,$xml_tree->quiztype,next-ex-url");
+                                               if (is_array($next)) {
+                                                       $next = $next[0]; // in case "next-ex-url" was repeated in the xml file
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               $hp->nextexercise = $next;
+               $hp->exercisetitle = (empty($title) || is_array($title)) ? basename($filepath) : $title;
+               $hp->exercisesubtitle = (empty($subtitle) || is_array($subtitle)) ? $hp->exercisetitle : $subtitle;
+       }
+}
+function hotpot_get_all_instances_in_course($modulename, $course) {
+/// called from index.php
+
+       global $CFG;
+       $instances = array();
+
+       if (isset($CFG->release) && substr($CFG->release, 0, 3)>=1.2) {
+               $groupmode = 'cm.groupmode,';
+       } else {
+               $groupmode = '';
+       }
+
+       $query = "
+               SELECT
+                       cm.id AS coursemodule,
+                       cm.course AS course,
+                       cm.module AS module,
+                       cm.instance AS instance,
+                       -- cm.section AS section,
+                       cm.visible AS visible,
+                       $groupmode
+                       -- cs.section AS sectionnumber,
+                       cs.section AS section,
+                       cs.sequence AS sequence,
+                       cs.visible AS sectionvisible,
+                       thismodule.*
+               FROM
+                       {$CFG->prefix}course_modules AS cm,
+                       {$CFG->prefix}course_sections AS cs,
+                       {$CFG->prefix}modules AS m,
+                       {$CFG->prefix}$modulename AS thismodule
+               WHERE
+                       m.name = '$modulename' AND
+                       m.id = cm.module AND
+                       cm.course = '$course->id' AND
+                       cm.section = cs.id AND
+                       cm.instance = thismodule.id
+       ";
+       if ($rawmods = get_records_sql($query)) {
+
+               // cache $isteacher setting
+               $isteacher = isteacher($course->id);
+
+               $explodesection = array();
+               $order = array();
+
+               foreach ($rawmods as $rawmod) {
+
+                       if (empty($explodesection[$rawmod->section])) {
+                               $explodesection[$rawmod->section] = true;
+
+                               $coursemodules = explode(',', $rawmod->sequence);
+                               foreach ($coursemodules as $i=>$coursemodule) {
+                                       $order[$coursemodule] = sprintf('%d.%04d', $rawmod->section, $i);
+                               }
+                       }
+
+                       if ($isteacher) {
+                               $visible = true;
+                       } else if ($modulename=='hotpot') {
+                               $visible = hotpot_is_visible($rawmod);
+                       } else {
+                               $visible = $rawmod->visible;
+                       }
+
+                       if ($visible) {
+                               $instances[$order[$rawmod->coursemodule]] = $rawmod;
+                       }
+
+               } // end foreach $modinfo
+
+               ksort($instances);
+               $instances = array_values($instances);
+       }
+
+       return $instances;
+}
+
+function hotpot_update_chain(&$hp) {
+/// update a chain of hotpot actiivities
+
+       $ok = true;
+       if ($hotpot_modules = hotpot_get_chain($hp)) {
+
+               // skip updating of these fields
+               $skipfields = array('id', 'course', 'name', 'reference', 'summary', 'shownextquiz');
+               $fields = array();
+
+               foreach ($hotpot_modules as $hotpot_module) {
+
+                       if ($hp->instance==$hotpot_module->id) {
+                               // don't need to update this hotpot
+
+                       } else {
+                               // shortcut to hotpot record
+                               $hotpot = &$hotpot_module->hotpot;
+
+                               // get a list of fields to update (first time only)
+                               if (empty($fields)) {
+                                       $fields = array_keys(get_object_vars($hotpot));
+                               }
+
+                               // assume update is NOT required
+                               $require_update = false;
+
+                               // update field values (except $skipfields)
+                               foreach($fields as $field) {
+                                       if (in_array($field, $skipfields) || $hotpot->$field==$hp->$field) {
+                                               // update not required for this field
+                                       } else {
+                                               $require_update = true;
+                                               $hotpot->$field = $hp->$field;
+                                       }
+                               }
+
+                               // update this $hotpot, if required
+                               if ($require_update && !update_record("hotpot", $hotpot)) {
+                                       error("Could not update the $hp->modulename", "view.php?id=$hp->course");
+                               }
+                       }
+               } // end foreach $ids
+       }
+       return $ok;
+}
+function hotpot_delete_instance($id) {
+/// Given an ID of an instance of this module,
+/// this function will permanently delete the instance
+/// and any data that depends on it.
+
+       $result = false;
+       if (delete_records("hotpot", "id", "$id")) {
+               $result = true;
+               delete_records("hotpot_questions", "hotpot", "$id");
+               if ($attempts = get_records_select("hotpot_attempts", "hotpot='$id'")) {
+                       $ids = implode(',', array_keys($attempts));
+                       delete_records_select("hotpot_attempts",  "id IN ($ids)");
+                       delete_records_select("hotpot_details",   "attempt IN ($ids)");
+                       delete_records_select("hotpot_responses", "attempt IN ($ids)");
+               }
+       }
+       return $result;
+}
+function hotpot_delete_and_notify($table, $select, $strtable) {
+       $count = max(0, count_records_select($table, $select));
+       if ($count) {
+               delete_records_select($table, $select);
+               $count -= max(0, count_records_select($table, $select));
+               if ($count) {
+                       notify(get_string('deleted')." $count x $strtable");
+               }
+       }
+}
+
+function hotpot_user_complete($course, $user, $mod, $hp) {
+/// Print a detailed representation of what a  user has done with
+/// a given particular instance of this module, for user activity reports.
+
+       $report = hotpot_user_outline($course, $user, $mod, $hp);
+       if (empty($report)) {
+               print get_string("noactivity", "hotpot");
+       } else {
+               $date = userdate($report->time, get_string('strftimerecentfull'));
+               print $report->info.' '.get_string('mostrecently').': '.$date;
+       }
+       return true;
+}
+
+function hotpot_user_outline($course, $user, $mod, $hp) {
+/// Return a small object with summary information about what a
+/// user has done with a given particular instance of this module
+/// Used for user activity reports.
+/// $report->time = the time they did it
+/// $report->info = a short text description
+
+       $report = NULL;
+       if ($records = get_records_select("hotpot_attempts", "hotpot='$hp->id' AND userid='$user->id'", "timestart ASC", "*")) {
+               $scores = array();
+               foreach ($records as $record){
+                       if (empty($report->time)) {
+                               $report->time = $record->timestart;
+                       }
+                       $scores[] = hotpot_format_score($record);
+               }
+               if (empty($scores)) {
+                       $report->time = 0;
+                       $report->info = get_string('noactivity', 'hotpot');
+               } else {
+                       $report->info = get_string('score', 'quiz').': '.implode(', ', $scores);
+               }
+       }
+       return $report;
+}
+
+function hotpot_format_score($record, $undefined='&nbsp;') {
+       if (isset($record->score)) {
+               $score = $record->score;
+       } else {
+               $score = $undefined;
+       }
+       return $score;
+}
+
+function hotpot_format_status($record, $undefined='&nbsp;') {
+       global $HOTPOT_STATUS;
+
+       if (isset($record->status) || isset($HOTPOT_STATUS[$record->status])) {
+               $status = $HOTPOT_STATUS[$record->status];
+       } else {
+               $status = $undefined;
+       }
+       return $status;
+}
+
+function hotpot_print_recent_activity($course, $isteacher, $timestart) {
+/// Given a course and a time, this module should find recent activity
+/// that has occurred in hotpot activities and print it out.
+/// Return true if there was output, or false is there was none.
+
+       global $CFG;
+
+       $result = false;
+       if($isteacher){
+
+               $records = get_records_sql("
+                       SELECT
+                               h.id AS id,
+                               h.name AS name,
+                               COUNT(*) AS count_attempts
+                       FROM
+                               {$CFG->prefix}hotpot AS h,
+                               {$CFG->prefix}hotpot_attempts AS a
+                       WHERE
+                               h.course = $course->id
+                               AND h.id = a.hotpot
+                               AND a.id = a.clickreportid
+                               AND a.starttime > $timestart
+                       GROUP  BY
+                               h.id, h.name
+               ");
+               // note that PostGreSQL requires h.name in the GROUP BY clause
+
+               if($records) {
+
+                       $names = array();
+                       foreach ($records as $id => $record){
+                               $href = "$CFG->wwwroot/mod/hotpot/view.php?hp=$id";
+                               $name = '&nbsp;<a href="'.$href.'">'.$record->name.'</a>';
+                               if ($record->count_attempts > 1) {
+                                       $name .= " ($record->count_attempts)";
+                               }
+                               $names[] = $name;
+                       }
+
+                       print_headline(get_string('modulenameplural', 'hotpot').':');
+
+                       if ($CFG->version >= 2005050500) { // Moodle 1.5+
+                               echo '<div class="head"><div class="name">'.implode('<br />', $names).'</div></div>';
+                       } else { // Moodle 1.4.x (or less)
+                               echo '<font size="1">'.implode('<br />', $names).'</font>';
+                       }
+
+                       $result = true;
+               }
+       }
+       return $result;  //  True if anything was printed, otherwise false
+}
+
+function hotpot_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $cmid="", $userid="", $groupid="") {
+// Returns all quizzes since a given time.
+
+       global $CFG;
+
+       // If $cmid or $userid are specified, then this restricts the results
+       $cm_select = empty($cmid) ? "" : " AND cm.id = '$cmid'";
+       $user_select = empty($userid) ? "" : " AND u.id = '$userid'";
+
+       $records = get_records_sql("
+               SELECT
+                       a.*,
+                       h.name, h.course,
+                       cm.instance, cm.section,
+                       u.firstname, u.lastname, u.picture
+               FROM
+                       {$CFG->prefix}hotpot_attempts AS a,
+                       {$CFG->prefix}hotpot AS h,
+                       {$CFG->prefix}course_modules AS cm,
+                       {$CFG->prefix}user AS u
+               WHERE
+                       a.timefinish > '$sincetime'
+                       AND a.id = a.clickreportid
+                       AND a.userid = u.id $user_select
+                       AND a.hotpot = h.id $cm_select
+                       AND cm.instance = h.id
+                       AND cm.course = '$courseid'
+                       AND h.course = cm.course
+               ORDER BY
+                       a.timefinish ASC
+       ");
+
+       if (!empty($records)) {
+               foreach ($records as $record) {
+                       if (empty($groupid) || ismember($groupid, $record->userid)) {
+
+                               unset($activity);
+
+                               $activity->type = "hotpot";
+                               $activity->defaultindex = $index;
+                               $activity->instance = $record->hotpot;
+
+                               $activity->name = $record->name;
+                               $activity->section = $record->section;
+
+                               $activity->content->attemptid = $record->id;
+                               $activity->content->attempt = $record->attempt;
+                               $activity->content->score = $record->score;
+                               $activity->content->timestart = $record->timestart;
+                               $activity->content->timefinish = $record->timefinish;
+
+                               $activity->user->userid = $record->userid;
+                               $activity->user->fullname = fullname($record);
+                               $activity->user->picture = $record->picture;
+
+                               $activity->timestamp = $record->timefinish;
+
+                               $activities[] = $activity;
+
+                               $index++;
+                       }
+               } // end foreach
+       }
+}
+
+function hotpot_print_recent_mod_activity($activity, $course, $detail=false) {
+/// Basically, this function prints the results of "hotpot_get_recent_activity"
+
+       global $CFG, $THEME, $USER;
+
+       print '<table border="0" cellpadding="3" cellspacing="0">';
+
+       print '<tr><td bgcolor="'.$THEME->cellcontent2.'" class="forumpostpicture" width="35" valign="top">';
+       print_user_picture($activity->user->userid, $course, $activity->user->picture);
+       print '</td><td width="100%"><font size="2">';
+
+       if ($detail) {
+               // activity icon
+               $src = "$CFG->modpixpath/$activity->type/icon.gif";
+               print '<img src="'.$src.'" height="16" width="16" alt="'.$activity->type.'" /> ';
+
+               // link to activity
+               $href = "$CFG->wwwroot/mod/hotpot/view.php?hp=$activity->instance";
+               print '<a href="'.$href.'">'.$activity->name.'</a> - ';
+       }
+       if (isteacher($course)) {
+               // score (with link to attempt details)
+               $href = "$CFG->wwwroot/mod/hotpot/review.php?hp=$activity->instance&attempt=".$activity->content->attemptid;
+               print '<a href="'.$href.'">('.hotpot_format_score($activity->content).')</a> ';
+
+               // attempt number
+               print get_string('attempt', 'quiz').' - '.$activity->content->attempt.'<br />';
+       }
+
+       // link to user
+       $href = "$CFG->wwwroot/user/view.php?id=$activity->user->userid&course=$course";
+       print '<a href="'.$href.'">'.$activity->user->fullname.'</a> ';
+
+       // time and date
+       print ' - ' . userdate($activity->timestamp);
+
+       // duration
+       $duration = format_time($activity->content->timestart - $activity->content->timefinish);
+       print " &nbsp; ($duration)";
+
+       print "</font></td></tr>";
+       print "</table>";
+}
+
+function hotpot_cron () {
+/// Function to be run periodically according to the moodle cron
+/// This function searches for things that need to be done, such
+/// as sending out mail, toggling flags etc ...
+
+       global $CFG;
+
+       return true;
+}
+
+function hotpot_grades($hotpotid) {
+/// Must return an array of grades for a given instance of this module,
+/// indexed by user.  It also returns a maximum allowed grade.
+
+       $hotpot = get_record('hotpot', 'id', $hotpotid);
+       $return->grades = hotpot_get_grades($hotpot);
+       $return->maxgrade = $hotpot->grade;
+
+       return $return;
+}
+function hotpot_get_grades($hotpot, $user_ids='') {
+       global $CFG;
+
+       $grades = array();
+
+       $weighting = $hotpot->grade / 100;
+       $precision = hotpot_get_precision($hotpot);
+
+       // set the SQL string to determine the $grade
+       $grade = "";
+       switch ($hotpot->grademethod) {
+               case HOTPOT_GRADEMETHOD_HIGHEST:
+                       $grade = "ROUND(MAX(score) * $weighting, $precision) AS grade";
+                       break;
+               case HOTPOT_GRADEMETHOD_AVERAGE:
+                       // the 'AVG' function skips abandoned quizzes, so use SUM(score)/COUNT(id)
+                       $grade = "ROUND(SUM(score)/COUNT(id) * $weighting, $precision) AS grade";
+                       break;
+               case HOTPOT_GRADEMETHOD_FIRST:
+                       if ($CFG->dbtype=='postgres7') {
+                               $grade = "MIN(timestart||'_'||(CASE WHEN (score IS NULL) THEN '' ELSE TRIM(ROUND(score * $weighting, $precision)) END)) AS grade";
+                       } else {
+                               $grade = "MIN(CONCAT(timestart, '_', IF(score IS NULL, '', ROUND(score * $weighting, $precision)))) AS grade";
+                       }
+                       break;
+               case HOTPOT_GRADEMETHOD_LAST:
+                       if ($CFG->dbtype=='postgres7') {
+                               $grade = "MAX(timestart||'_'||(CASE WHEN (score IS NULL) THEN '' ELSE TRIM(ROUND(score * $weighting, $precision)) END)) AS grade";
+                       } else {
+                               $grade = "MAX(CONCAT(timestart, '_', IF(score IS NULL, '', ROUND(score * $weighting, $precision)))) AS grade";
+                       }
+                       break;
+       }
+
+       if ($grade) {
+               $userid_condition = empty($user_ids) ? '' : "AND userid IN ($user_ids) ";
+               $grades = get_records_sql_menu("
+                       SELECT userid, $grade
+                       FROM {$CFG->prefix}hotpot_attempts
+                       WHERE timefinish>0 AND hotpot='$hotpot->id' $userid_condition
+                       GROUP BY userid
+               ");
+               if ($grades) {
+                       if ($hotpot->grademethod==HOTPOT_GRADEMETHOD_FIRST || $hotpot->grademethod==HOTPOT_GRADEMETHOD_LAST) {
+                               // remove left hand characters in $grade (up to and including the underscore)
+                               foreach ($grades as $userid=>$grade) {
+                                       $grades[$userid] = substr($grades[$userid], strpos($grades[$userid], '_')+1);
+                               }
+                       }
+               }
+       }
+
+       return $grades;
+}
+function hotpot_get_precision(&$hotpot) {
+       return ($hotpot->grademethod==HOTPOT_GRADEMETHOD_AVERAGE || $hotpot->grade<100) ? 1 : 0;
+}
+
+function hotpot_get_participants($hotpotid) {
+//Must return an array of user ids who are participants
+//for a given instance of hotpot. Must include every user involved
+//in the instance, independient of his role (student, teacher, admin...)
+//See other modules as example.
+       global $CFG;
+
+       return get_records_sql("
+               SELECT DISTINCT
+                       u.id, u.id
+               FROM
+                       {$CFG->prefix}user u,
+                       {$CFG->prefix}hotpot_attempts a
+               WHERE
+                       u.id = a.userid
+                       AND a.hotpot = '$hotpotid'
+       ");
+}
+
+function hotpot_scale_used ($hotpotid, $scaleid) {
+//This function returns if a scale is being used by one hotpot
+//it it has support for grading and scales. Commented code should be
+//modified if necessary. See forum, glossary or journal modules
+//as reference.
+
+       $report = false;
+
+       //$rec = get_record("hotpot","id","$hotpotid","scale","-$scaleid");
+       //
+       //if (!empty($rec)  && !empty($scaleid)) {
+       //      $report = true;
+       //}
+
+       return $report;
+}
+
+//////////////////////////////////////////////////////////
+/// Any other hotpot functions go here.
+/// Each of them must have a name that starts with hotpot
+
+
+function hotpot_add_attempt($hotpotid) {
+       global $db, $CFG, $USER;
+       $time = time();
+       switch (strtolower($CFG->dbtype)) {
+               case 'mysql':
+                       $timefinish = "IF(a.timefinish IS NULL, '$time', a.timefinish)";
+                       $clickreportid = "IF(a.clickreportid IS NULL, a.id, a.clickreportid)";
+                       break;
+               case 'postgres7':
+                       $timefinish = "WHEN(a.timefinish IS NULL) THEN '$time' ELSE a.timefinish";
+                       $clickreportid = "WHEN(a.clickreportid IS NULL) THEN a.id ELSE a.clickreportid";
+                       break;
+       }
+
+       // set all previous "in progress" attempts at this quiz to "abandoned"
+       $db->Execute("
+               UPDATE
+                       {$CFG->prefix}hotpot_attempts as a
+               SET
+                       a.timefinish = $timefinish,
+                       a.status = '".HOTPOT_STATUS_ABANDONED."',
+                       a.clickreportid = $clickreportid
+               WHERE
+                       a.hotpot='$hotpotid'
+                       AND a.userid='$USER->id'
+                       AND a.status='".HOTPOT_STATUS_INPROGRESS."'
+       ");
+
+       // create and add new attempt record
+       $attempt->hotpot = $hotpotid;
+       $attempt->userid = $USER->id;
+       $attempt->attempt = hotpot_get_next_attempt($hotpotid);
+       $attempt->timestart = time();
+
+       return insert_record("hotpot_attempts", $attempt);
+}
+function hotpot_get_next_attempt($hotpotid) {
+       global $USER;
+
+       // get max attempt so far
+       $i = count_records_select('hotpot_attempts', "hotpot='$hotpotid' AND userid='$USER->id'", 'MAX(attempt)');
+
+       return empty($i) ? 1 : ($i+1);
+}
+function hotpot_get_question_name($question) {
+       $name = '';
+       if (isset($question->text)) {
+               $name = hotpot_strings($question->text);
+       }
+       if (empty($name)) {
+               $name = $question->name;
+       }
+       return $name;
+}
+function hotpot_strings($ids) {
+
+       // array of ids of empty strings
+       static $HOTPOT_EMPTYSTRINGS;
+
+       if (!isset($HOTPOT_EMPTYSTRINGS)) { // first time only
+               // get ids of empty strings
+               $emptystrings = get_records_select('hotpot_strings', 'LENGTH(TRIM(string))=0');
+               $HOTPOT_EMPTYSTRINGS = empty($emptystrings) ? array() : array_keys($emptystrings);
+       }
+
+       $strings = array();
+       if (!empty($ids)) {
+               $ids = explode(',', $ids);
+               foreach ($ids as $id) {
+                       if (!in_array($id, $HOTPOT_EMPTYSTRINGS)) {
+                               $strings[] = hotpot_string($id);
+                       }
+               }
+       }
+       return implode(',', $strings);
+}
+function hotpot_string($id) {
+       return get_field('hotpot_strings', 'string', 'id', $id);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+/// the class definitions to handle XML trees
+
+// get the standard XML parser supplied with Moodle
+require_once("$CFG->libdir/xmlize.php");
+
+// get the default class for hotpot quiz templates
+require_once("$CFG->hotpottemplate/default.php");
+
+class hotpot_xml_tree {
+       function hotpot_xml_tree($str, $xml_root='') {
+               if (empty($str)) {
+                       $this->xml =  array();
+               } else {
+                       if (empty($CFG->unicodedb)) {
+                               $str = utf8_encode($str);
+                       }
+                       $this->xml =  xmlize($str, 0);
+               }
+               $this->xml_root = $xml_root;
+       }
+       function xml_value($tags, $more_tags="[0]['#']") {
+
+               $tags = empty($tags) ? '' : "['".str_replace(",", "'][0]['#']['", $tags)."']";
+               eval('$value = &$this->xml'.$this->xml_root.$tags.$more_tags.';');
+
+               if (is_string($value)) {
+                       if (empty($CFG->unicodedb)) {
+                               $value = utf8_decode($value);
+                       }
+
+                       // decode angle brackets
+                       $value = strtr($value, array('&#x003C;'=>'<', '&#x003E;'=>'>', '&#x0026;'=>'&'));
+
+                       // remove white space between <table>, <ul|OL|DL> and <OBJECT|EMBED> parts
+                       // (so it doesn't get converted to <br />)
+                       $htmltags = '('
+                       .       'TABLE|/?CAPTION|/?COL|/?COLGROUP|/?TBODY|/?TFOOT|/?THEAD|/?TD|/?TH|/?TR'
+                       .       '|OL|UL|/?LI'
+                       .       '|DL|/?DT|/?DD'
+                       .       '|EMBED|OBJECT|APPLET|/?PARAM'
+                       //.     '|SELECT|/?OPTION'
+                       //.     '|FIELDSET|/?LEGEND'
+                       //.     '|FRAMESET|/?FRAME'
+                       .       ')'
+                       ;
+
+                       $space = '(\s|(<br[^>]*>))+';
+                       $search = '#(<'.$htmltags.'[^>]*'.'>)'.$space.'(?='.'<)#is';
+                       $value = preg_replace($search, '\\1', $value);
+
+                       // replace remaining newlines with <br />
+                       $value = str_replace("\n", '<br />', $value);
+
+                       // encode unicode characters as HTML entities
+                       // (in particular, accented charaters that have not been encoded by HP)
+
+                       // unicode characters can be detected by checking the hex value of a character
+                       //      00 - 7F : ascii char (roman alphabet + punctuation)
+                       //      80 - BF : byte 2, 3 or 4 of a unicode char
+                       //      C0 - DF : 1st byte of 2-byte char
+                       //      E0 - EF : 1st byte of 3-byte char
+                       //      F0 - FF : 1st byte of 4-byte char
+                       // if the string doesn't match the above, it might be
+                       //      80 - FF : single-byte, non-ascii char
+                       $search = '#('.'[\xc0-\xdf][\x80-\xbf]'.'|'.'[\xe0-\xef][\x80-\xbf]{2}'.'|'.'[\xf0-\xff][\x80-\xbf]{3}'.'|'.'[\x80-\xff]'.')#se';
+                       $value = preg_replace($search, "hotpot_utf8_to_html_entity('\\1')", $value);
+               }
+               return $value;
+       }
+       function xml_values($tags) {
+               $i = 0;
+               $values = array();
+               while ($value = $this->xml_value($tags, "[$i]['#']")) {
+                       $values[$i++] = $value;
+               }
+               return $values;
+       }
+       function obj_value(&$obj, $name) {
+               return is_object($obj) ? @$obj->$name : (is_array($obj) ? @$obj[$name] : NULL);
+       }
+       function encode_cdata(&$str, $tag) {
+
+               // conversion tables
+               static $HTML_ENTITIES = array(
+                       '&apos;' => "'",
+                       '&quot;' => '"',
+                       '&lt;'   => '<',
+                       '&gt;'   => '>',
+                       '&amp;'  => '&',
+               );
+               static $ILLEGAL_STRINGS = array(
+                       "\r"  => '',
+                       "\n"  => '&lt;br /&gt;',
+                       ']]>' => '&#93;&#93;&#62;',
+               );
+
+               // extract the $tag from the $str(ing), if possible
+               $pattern = '|(^.*<'.$tag.'[^>]*)(>.*<)(/'.$tag.'>.*$)|is';
+               if (preg_match($pattern, $str, $matches)) {
+
+                       // encode problematic CDATA chars and strings
+                       $matches[2] = strtr($matches[2], $ILLEGAL_STRINGS);
+
+                       // if there are any ampersands in "open text"
+                       // surround them by CDATA start and end markers
+                       // (and convert HTML entities to plain text)
+                       $search = '/>([^<]*&[^<]*)</e';
+                       $replace = '"><![CDATA[".strtr("$1", $HTML_ENTITIES)."]]><"';
+                       $matches[2] = preg_replace($search, $replace, $matches[2]);
+
+                       $str = $matches[1].$matches[2].$matches[3];
+               }
+       }
+}
+
+class hotpot_xml_quiz extends hotpot_xml_tree {
+
+       // constructor function
+       function hotpot_xml_quiz(&$obj, $read_file=true, $parse_xml=true, $convert_urls=true, $report_errors=true, $create_html=true) {
+               // obj can be the $_GET array or a form object/array
+
+               global $CFG, $HOTPOT_OUTPUTFORMAT, $HOTPOT_OUTPUTFORMAT_DIR;
+
+               // check xmlize functions are available
+               if (! function_exists("xmlize")) {
+                       error('xmlize functions are not available');
+               }
+
+               $this->read_file = $read_file;
+               $this->parse_xml = $parse_xml;
+               $this->convert_urls = $convert_urls;
+               $this->report_errors = $report_errors;
+               $this->create_html = $create_html;
+
+               // extract fields from $obj
+               //      course     : the course id
+               //      reference       : the filename within the files folder
+               //      location         : "site" files folder or "course" files folder
+               //      navigation   : type of navigation required in quiz
+               //      forceplugins : force Moodle compatible media players
+               $this->course = $this->obj_value($obj, 'course');
+               $this->reference = $this->obj_value($obj, 'reference');
+               $this->location = $this->obj_value($obj, 'location');
+               $this->navigation = $this->obj_value($obj, 'navigation');
+               $this->forceplugins = $this->obj_value($obj, 'forceplugins');
+
+               // can't continue if there is no course or reference
+               if (empty($this->course) || empty($this->reference)) {
+                       $this->error = get_string('error_nocourseorfilename', 'hotpot');
+                       if ($this->report_errors) {
+                               error($this->error);
+                       }
+                       return;
+               }
+
+               $this->course_homeurl = "$CFG->wwwroot/course/view.php?id=$this->course";
+
+               // set filedir, filename and filepath
+               switch ($this->location) {
+                       case HOTPOT_LOCATION_SITEFILES:
+                               $site = get_site();
+                               $this->filedir = $site->id;
+                               break;
+
+                       case HOTPOT_LOCATION_COURSEFILES:
+                       default:
+                               $this->filedir = $this->course;
+                               break;
+               }
+               $this->filesubdir = dirname($this->reference);
+               if ($this->filesubdir=='.') {
+                       $this->filesubdir = '';
+               }
+               if ($this->filesubdir) {
+                       $this->filesubdir .= '/';
+               }
+               $this->filename = basename($this->reference);
+               $this->fileroot = "$CFG->dataroot/$this->filedir";
+               $this->filepath = "$this->fileroot/$this->reference";
+
+               // read the file, if required
+               if ($this->read_file) {
+
+                       if (!file_exists($this->filepath) || !is_readable($this->filepath)) {
+                               $this->error = get_string('error_couldnotopensourcefile', 'hotpot', $this->filepath);
+                               if ($this->report_errors) {
+                                       error($this->error, $this->course_homeurl);
+                               }
+                               return;
+                       }
+
+                       // read in the XML source
+                       $this->source = file_get_contents($this->filepath);
+
+                       // convert relative URLs to absolute URLs
+                       if ($this->convert_urls) {
+                               $this->hotpot_convert_relative_urls($this->source);
+                       }
+
+                       $this->html = '';
+                       $this->quiztype = '';
+                       $this->outputformat = 0;
+
+                       // is this an html file?
+                       if (preg_match('|\.html?$|', $this->filename)) {
+
+                               $this->filetype = 'html';
+                               $this->html = &$this->source;
+
+                               // relative URLs in stylesheets
+                               $search = '|'.'(<style[^>]*>)'.'(.*?)'.'(</style>)'.'|ise';
+                               $replace = "stripslashes('\\1').hotpot_convert_stylesheets_urls('".$this->get_baseurl()."','".$this->reference."','\\2'.'\\3')";
+                               $this->source = preg_replace($search, $replace, $this->source);
+
+                               // relative URLs in "PreloadImages(...);"
+                               $search = '|'.'(?<='.'PreloadImages'.'\('.')'."([^)]+?)".'(?='.'\);'.')'.'|se';
+                               $replace = "hotpot_convert_preloadimages_urls('".$this->get_baseurl()."','".$this->reference."','\\1')";
+                               $this->source = preg_replace($search, $replace, $this->source);
+
+                               // relative URLs in <button class="NavButton" ... onclick="location='...'">
+                               $search = '|'.'(?<='.'onclick="'."location='".')'."([^']*)".'(?='."'; return false;".'")'.'|ise';
+                               $replace = "hotpot_convert_navbutton_url('".$this->get_baseurl()."','".$this->reference."','\\1','".$this->course."')";
+                               $this->source = preg_replace($search, $replace, $this->source);
+
+                       } else {
+                               if ($this->parse_xml) {
+
+                                       $this->filetype = 'xml';
+
+                                       // encode "gap fill" text in JCloze exercise
+                                       $this->encode_cdata($this->source, 'gap-fill');
+
+                                       // convert source to xml tree
+                                       $this->hotpot_xml_tree($this->source);
+
+                                       $keys = array_keys($this->xml);
+                                       foreach ($keys as $key) {
+                                               if (preg_match('/^(hotpot|textoys)-(\w+)-file$/i', $key, $matches)) {
+                                                       $this->quiztype = strtolower($matches[2]);
+                                                       $this->xml_root = "['$key']['#']";
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               if ($this->create_html) {
+
+                                       // set the real output format from the requested output format
+                                       $this->real_outputformat = $this->obj_value($obj, 'outputformat');
+                                       $this->draganddrop = '';
+                                       if (
+                                               empty($this->real_outputformat) ||
+                                               $this->real_outputformat==HOTPOT_OUTPUTFORMAT_BEST ||
+                                               empty($HOTPOT_OUTPUTFORMAT_DIR[$this->real_outputformat])
+                                       ) {
+                                               if ($CFG->hotpotismobile && isset($HOTPOT_OUTPUTFORMAT_DIR[HOTPOT_OUTPUTFORMAT_MOBILE])) {
+                                                               $this->real_outputformat = HOTPOT_OUTPUTFORMAT_MOBILE;
+                                               } else { // PC
+                                                       if ($this->quiztype=='jmatch' || $this->quiztype=='jmix') {
+                                                               $this->real_outputformat = HOTPOT_OUTPUTFORMAT_V6_PLUS;
+                                                       } else {
+                                                               $this->real_outputformat = HOTPOT_OUTPUTFORMAT_V6;
+                                                       }
+                                               }
+                                       }
+
+                                       if ($this->real_outputformat==HOTPOT_OUTPUTFORMAT_V6_PLUS) {
+                                               if ($this->quiztype=='jmatch' || $this->quiztype=='jmix') {
+                                                       $this->draganddrop = 'd'; // prefix for templates (can also be "f" ?)
+                                               }
+                                               $this->real_outputformat = HOTPOT_OUTPUTFORMAT_V6;
+                                       }
+
+                                       // set path(s) to template
+                                       $this->template_dir = $HOTPOT_OUTPUTFORMAT_DIR[$this->real_outputformat];
+                                       $this->template_dirpath = $CFG->hotpottemplate.'/'.$this->template_dir;
+                                       $this->template_filepath = $CFG->hotpottemplate.'/'.$this->template_dir.'.php';
+
+                                       // check template class exists
+                                       if (!file_exists($this->template_filepath) || !is_readable($this->template_filepath)) {
+                                               $this->error = get_string('error_couldnotopentemplate', 'hotpot', $this->template_dir);
+                                               if ($this->report_errors) {
+                                                       error($this->error, $this->course_homeurl);
+                                               }
+                                               return;
+                                       }
+
+                                       // get default and output-specfic template classes
+                                       include($this->template_filepath);
+
+                                       // create html (using the template for the specified output format)
+                                       $this->template = new hotpot_xml_quiz_template($this);
+                                       $this->html = &$this->template->html;
+
+                               } // end $this->create_html
+                       } // end if html/xml file
+               } // end if $this->read_file
+       } // end constructor function
+
+       function hotpot_convert_relative_urls(&$str) {
+               $tagopen = '(?:(<)|(&lt;)|(&amp;#x003C;))'; // left angle bracket
+               $tagclose = '(?(2)>|(?(3)&gt;|(?(4)&amp;#x003E;)))'; //  right angle bracket (to match left angle bracket)
+
+               $space = '\s+'; // at least one space
+               $anychar = '(?:[^>]*?)'; // any character
+
+               $quoteopen = '("|&quot;|&amp;quot;)'; // open quote
+               $quoteclose = '\\5'; //  close quote (to match open quote)
+
+               $replace = "hotpot_convert_relative_url('".$this->get_baseurl()."', '".$this->reference."', '\\1', '\\6', '\\7')";
+
+               $tags = array('script'=>'src', 'link'=>'href', 'a'=>'href','img'=>'src','param'=>'value', 'object'=>'data', 'embed'=>'src');
+               foreach ($tags as $tag=>$attribute) {
+                       if ($tag=='param') {
+                               $url = '\S+?\.\S+?'; // must include a filename and have no spaces
+                       } else {
+                               $url = '.*?';
+                       }
+                       $search = "%($tagopen$tag$space$anychar$attribute=$quoteopen)($url)($quoteclose$anychar$tagclose)%ise";
+                       $str = preg_replace($search, $replace, $str);
+               }
+       }
+
+       function get_baseurl() {
+               // set the url base (first time only)
+               if (!isset($this->baseurl)) {
+                       global $CFG;
+                       if ($CFG->slasharguments) {
+                               $this->baseurl = "$CFG->wwwroot/file.php/$this->filedir/";
+                       } else {
+                               $this->baseurl = "$CFG->wwwroot/file.php?file=/$this->filedir/";
+                       }
+               }
+               return $this->baseurl;
+       }
+
+
+       // insert forms and messages
+
+       function remove_nav_buttons() {
+               $search = '#<!-- Begin(Top|Bottom)NavButtons -->(.*?)<!-- End(Top|Bottom)NavButtons -->#s';
+               $this->html = preg_replace($search, '', $this->html);
+       }
+       function insert_script($src=HOTPOT_JS) {
+               $script = '<script src="'.$src.'" type="text/javascript" language="javascript"></script>'."\n";
+               $this->html = preg_replace('|</head>|i', $script.'</head>', $this->html, 1);
+       }
+       function insert_submission_form($attemptid, $startblock, $endblock, $keep_contents=false) {
+               $form_name = 'store';
+               $form_fields = ''
+               .       '<input type="hidden" name="attemptid" value="'.$attemptid.'" />'
+               .       '<input type="hidden" name="starttime" value="" />'
+               .       '<input type="hidden" name="endtime" value="" />'
+               .       '<input type="hidden" name="mark" value="" />'
+               .       '<input type="hidden" name="detail" value="" />'
+               .       '<input type="hidden" name="status" value="" />'
+               ;
+               $this->insert_form($startblock, $endblock, $form_name, $form_fields, $keep_contents);
+       }
+       function insert_giveup_form($attemptid, $startblock, $endblock, $keep_contents=false) {
+               $form_name = 'giveup';
+               $form_fields = ''
+               .       '<input type="hidden" name="attemptid" value="'.$attemptid.'" />'
+               .       '<input type="hidden" name="status" value="'.HOTPOT_STATUS_ABANDONED.'" />'
+               .       '<input type="submit" value="'.get_string('giveup', 'hotpot').'" class="FuncButton" onfocus="FuncBtnOver(this)" onblur="FuncBtnOut(this)" onmouseover="FuncBtnOver(this)" onmouseout="FuncBtnOut(this)" onmousedown="FuncBtnDown(this)" onmouseup="FuncBtnOut(this)" />'
+               ;
+               $this->insert_form($startblock, $endblock, $form_name, $form_fields, $keep_contents, true);
+       }
+       function insert_form($startblock, $endblock, $form_name, $form_fields, $keep_contents, $center=false) {
+               global $CFG;
+               $search = '#('.preg_quote($startblock).')(.*?)('.preg_quote($endblock).')#s';
+               $replace = $form_fields;
+               if ($keep_contents) {
+                       $replace .= '\\2';
+               }
+               if ($form_name) {
+                       $replace = '<form action="'.$CFG->wwwroot.'/mod/hotpot/attempt.php" method="POST" name="'.$form_name.'" target="'.$CFG->framename.'">'.$replace.'</form>';
+               }
+               if ($center) {
+                       $replace = '<div style="margin-left:auto; margin-right:auto; text-align: center;">'.$replace.'</div>';
+               }
+               $replace = '\\1'.$replace.'\\3';
+               $this->html = preg_replace($search, $replace, $this->html, 1);
+       }
+       function insert_message($start_str, $message, $color='red', $align='center') {
+               $message = '<p align="'.$align.'" style="text-align:'.$align.'"><b><font color="'.$color.'">'.$message."</font></b></p>\n";
+               $this->html = preg_replace('|'.preg_quote($start_str).'|', $start_str.$message, $this->html, 1);
+       }
+
+       function adjust_media_urls() {
+
+               if ($this->forceplugins) {
+
+                       // make sure the Moodle media plugin is available
+                       global $CFG;
+                       include_once "$CFG->dirroot/filter/mediaplugin/filter.php";
+
+                       // exclude swf files from the filter
+                       //$CFG->filter_mediaplugin_ignore_swf = true;
+
+                       $space = '\s(?:.+\s)?';
+                       $quote = '["'."']?"; // single, double, or no quote
+
+                       // patterns to media files types and paths
+                       $filetype = "avi|mpeg|mpg|mp3|mov|wmv";
+                       $filepath = ".*?\.($filetype)";
+
+                       $tagopen = '(?:(<)|(\\\\u003C))'; // left angle-bracket (uses two parenthese)
+                       $tagclose = '(?(1)>|(?(2)\\\\u003E))'; // right angle-bracket (to match the left one)
+                       $tagreopen = '(?(1)<|(?(2)\\\\u003C))'; // another left angle-bracket (to match the first one)
+
+                       // pattern to match <PARAM> tags which contain the file path
+                       //      wmp        : url
+                       //      quicktime  : src
+                       //      realplayer : src
+                       //      flash      : movie (doesn't need replacing)
+                       $param_url = "/{$tagopen}param{$space}name=$quote(?:movie|src|url)$quote{$space}value=$quote($filepath)$quote.*?$tagclose/is";
+
+                       // pattern to match <a> tags which link to multimedia files
+                       $link_url = "/{$tagopen}a{$space}href=$quote($filepath)$quote.*?$tagclose.*?$tagreopen\/A$tagclose/is";
+
+                       // extract <object> tags
+                       preg_match_all("/{$tagopen}object\s.*?{$tagclose}(.*?){$tagreopen}\/object{$tagclose}/is", $this->html, $objects);
+
+                       $i_max = count($objects[0]);
+                       for ($i=0; $i<$i_max; $i++) {
+
+                               // extract URL from <PARAM> or <A> 
+                               $url = '';
+                               if (preg_match($param_url, $objects[3][$i], $matches) || preg_match($link_url, $objects[3][$i], $matches)) {
+                                       $url = $matches[3];
+                               }
+
+                               if ($url) {
+                                       // strip inner tags (e.g. <embed>)
+                                       $txt = preg_replace("/$tagopen.*?$tagclose/", '', $objects[3][$i]);
+
+                                       // if url is in the query string, remove the leading characters
+                                       $url = preg_replace('/^[^?]*\?([^=]+=[^&]*&)*[^=]+=([^&]*)$/', '$2', $url, 1);
+                                       $link = '<a href="'.$url.'">'.$txt.'</a>';
+
+                                       $new_object = mediaplugin_filter($this->filedir, $link);
+                                       $new_object = str_replace($link, '', $new_object);
+                                       $new_object = str_replace('&amp;', '&', $new_object);
+
+                                       $this->html = str_replace($objects[0][$i], $new_object, $this->html);
+                               }
+                       }
+               }
+       }
+
+} // end class
+
+function hotpot_convert_stylesheets_urls($baseurl, $reference, $css, $stripslashes=true) {
+       if ($stripslashes) {
+               $css = stripslashes($css);
+       }
+       $search = '|'.'(?<='.'url'.'\('.')'."(.+?)".'(?='.'\)'.')'.'|ise';
+       $replace = "hotpot_convert_url('".$baseurl."','".$reference."','\\1')";
+       return preg_replace($search, $replace, $css);
+}
+function hotpot_convert_preloadimages_urls($baseurl, $reference, $urls, $stripslashes=true) {
+       if ($stripslashes) {
+               $urls = stripslashes($urls);
+       }
+       $search = '|(?<=["'."'])([^,'".'"]*?)(?=["'."'])|ise";
+       $replace = "hotpot_convert_url('".$baseurl."','".$reference."','\\1')";
+       return preg_replace($search, $replace, $urls);
+}
+function hotpot_convert_navbutton_url($baseurl, $reference, $url, $course, $stripslashes=true) {
+       global $CFG;
+
+       if ($stripslashes) {
+               $url = stripslashes($url);
+       }
+       $url = hotpot_convert_url($baseurl, $reference, $url, false);
+
+       // is this a $url for another hotpot in this course ?
+       if (preg_match("|^$baseurl(.*)$|", $url, $matches)) {
+               if ($records = get_records_select('hotpot', "course='$course' AND reference='".$matches[1]."'")) {
+                       $ids = array_keys($records);
+                       $url = "$CFG->wwwroot/mod/hotpot/view.php?hp=".$ids[0];
+               }
+       }
+
+       return $url;
+}
+
+function hotpot_convert_relative_url($baseurl, $reference, $opentag, $url, $closetag, $stripslashes=true) {
+       if ($stripslashes) {
+               $opentag = stripslashes($opentag);
+               $url = stripslashes($url);
+               $closetag = stripslashes($closetag);
+       }
+
+       // catch <PARAM name="FlashVars" value="TheSound=soundfile.mp3">
+       //      ampersands can appear as "&", "&amp;" or "&amp;#x0026;amp;"
+       if (preg_match('|^'.'\w+=[^&]+'.'('.'&((amp;#x0026;)?amp;)?'.'\w+=[^&]+)*'.'$|', $url)) {
+               $query = $url;
+               $url = '';
+               $fragment = '';
+
+       // parse the $url into $matches
+       //      [1] path
+       //      [2] query string, if any
+       //      [3] anchor fragment, if any
+       } else if (preg_match('|^'.'([^?]*)'.'((?:\\?[^#]*)?)'.'((?:#.*)?)'.'$|', $url, $matches)) {
+               $url = $matches[1];
+               $query = $matches[2];
+               $fragment = $matches[3];
+
+       // these appears to be no query or fragment in this url
+       } else {
+               $query = '';
+               $fragment = '';
+       }
+
+       if ($url) {
+               $url = hotpot_convert_url($baseurl, $reference, $url, false);
+       }
+
+       if ($query) {
+               $search = '#'.'(file|src|thesound)='."([^&]+)".'#ise';
+               $replace = "'\\1='.hotpot_convert_url('".$baseurl."','".$reference."','\\2')";
+               $query = preg_replace($search, $replace, $query);
+       }
+
+       $url = $opentag.$url.$query.$fragment.$closetag;
+
+       return $url;
+}
+
+function hotpot_convert_url($baseurl, $reference, $url, $stripslashes=true) {
+       // maintain a cache of converted urls
+       static $HOTPOT_RELATIVE_URLS = array();
+
+       if ($stripslashes) {
+               $url = stripslashes($url);
+       }
+
+       // is this an absolute url? (or javascript pseudo url)
+       if (preg_match('%^(http://|/|javascript:)%i', $url)) {
+               // do nothing
+
+       // has this relative url already been converted?
+       } else if (isset($HOTPOT_RELATIVE_URLS[$url])) {
+               $url = $HOTPOT_RELATIVE_URLS[$url];
+
+       } else {
+               $relativeurl = $url;
+
+               // get the subdirectory, $dir, of the quiz $reference
+               $dir = dirname($reference);
+
+               // allow for leading "./" and "../"
+               while (preg_match('|^(\.{1,2})/(.*)$|', $url, $matches)) {
+                       if ($matches[1]=='..') {
+                               $dir = dirname($dir);
+                       }
+                       $url = $matches[2];
+               }
+
+               // add subdirectory, $dir, to $baseurl, if necessary
+               if ($dir && $dir<>'.') {
+                       $baseurl .= "$dir/";
+               }
+
+               // prefix $url with $baseurl
+               $url = "$baseurl$url";
+
+               // add url to cache
+               $HOTPOT_RELATIVE_URLS[$relativeurl] = $url;
+       }
+       return $url;
+}
+
+// ===================================================
+// function for adding attempt questions and responses
+// ===================================================
+
+function hotpot_add_attempt_details(&$attempt) {
+
+       // encode ampersands so that HTML entities are preserved in the XML parser
+       // N.B. ampersands inside <![CDATA[ ]]> blocks do NOT need to be encoded
+
+       $old = &$attempt->details; // shortcut to "old" details
+       $new = '';
+       $str_start = 0;
+       while (($cdata_start = strpos($old, '<![CDATA[', $str_start)) && ($cdata_end = strpos($old, ']]>', $cdata_start))) {
+               $cdata_end += 3;
+               $new .= str_replace('&', '&amp;', substr($old, $str_start, $cdata_start-$str_start)).substr($old, $cdata_start, $cdata_end-$cdata_start);
+               $str_start = $cdata_end;
+       }
+       $new .= str_replace('&', '&amp;', substr($old, $str_start));
+       unset($old);
+
+       // parse the attempt details as xml
+       $details = new hotpot_xml_tree($new, "['hpjsresult']['#']");
+
+       $num = -1;
+       $q_num = -1;
+       $question = NULL;
+       $reponse = NULL;
+
+       $i = 0;
+       $tags = 'fields,field';
+
+       while (($field="[$i]['#']") && $details->xml_value($tags, $field)) {
+
+               $name = $details->xml_value($tags, $field."['fieldname'][0]['#']");
+               $data = $details->xml_value($tags, $field."['fielddata'][0]['#']");
+
+               // parse the field name into $matches
+               //      [1] quiz type
+               //      [2] attempt detail name
+               if (preg_match('/^(\w+?)_(\w+)$/', $name, $matches)) {
+                       $quiztype = strtolower($matches[1]);
+                       $name = strtolower($matches[2]);
+
+                       // parse the attempt detail $name into $matches
+                       //      [1] question number
+                       //      [2] question detail name
+                       if (preg_match('/^q(\d+)_(\w+)$/', $name, $matches)) {
+                               $num = $matches[1];
+                               $name = strtolower($matches[2]);
+                               $data = addslashes($data);
+
+                               // adjust JCross question numbers
+                               if (preg_match('/^(across|down)(.*)$/', $name, $matches)) {
+                                       $num .= '_'.$matches[1]; // e.g. 01_across, 02_down
+                                       $name = $matches[2];
+                                       if (substr($name, 0, 1)=='_') {
+                                               $name = substr($name, 1); // remove leading '_'
+                                       }
+                               }
+
+                               // is this a new question (or the first one)?
+                               if ($q_num<>$num) {
+
+                                       // add previous question and response, if any
+                                       hotpot_add_response($attempt, $question, $response);
+
+                                       // initialize question object
+                                       $question = NULL;
+                                       $question->name = '';
+                                       $question->text = '';
+                                       $question->hotpot = $attempt->hotpot;
+
+                                       // initialize response object
+                                       $response = NULL;
+                                       $response->attempt = $attempt->id;
+
+                                       // update question number
+                                       $q_num = $num;
+                               }
+
+                               // adjust field name and value, and set question type
+                               // (may not be necessary one day)
+                               hotpot_adjust_response_field($quiztype, $question, $num, $name, $data);
+
+                               // add $data to the question/response details
+                               switch ($name) {
+                                       case 'name':
+                                       case 'type':
+                                               $question->$name = $data;
+                                               break;
+                                       case 'text':
+                                               $question->$name = hotpot_string_id($data);
+                                               break;
+
+                                       case 'correct':
+                                       case 'ignored':
+                                       case 'wrong':
+                                               $response->$name = hotpot_string_ids($data);
+                                               break;
+
+                                       case 'score':
+                                       case 'weighting':
+                                       case 'hints':
+                                       case 'clues':
+                                       case 'checks':
+                                               $response->$name = intval($data);
+                                               break;
+                               }
+
+                       } else { // attempt details
+
+                               // adjust field name and value
+                               hotpot_adjust_response_field($quiztype, $question, $num='', $name, $data);
+
+                               // add $data to the attempt details
+                               if ($name=='penalties') {
+                                       $attempt->$name = intval($data);
+                               }
+                       }
+               }
+
+               $i++;
+       } // end while
+
+       // add the final question and response, if any
+       hotpot_add_response($attempt, $question, $response);
+}
+function hotpot_add_response(&$attempt, &$question, &$response) {
+       global $db, $next_url;
+
+       $loopcount = 1;
+
+       $looping = isset($question) && isset($question->name) && isset($response);
+       while ($looping) {
+
+               if ($loopcount==1) {
+                       $questionname = $question->name;
+               }
+
+               if (!$question->id = get_field('hotpot_questions', 'id', 'name', $question->name, 'hotpot', $attempt->hotpot)) {
+                       // add question record
+                       if (!$question->id = insert_record('hotpot_questions', $question)) {
+                               error("Could not add question record (attempt_id=$attempt->id): ".$db->ErrorMsg(), $next_url);
+                       }
+               }
+
+               if (record_exists('hotpot_responses', 'attempt', $attempt->id, 'question', $question->id)) {
+                       // there is already a response to this question for this attempt
+                       // probably because this quiz has two questions with the same text
+                       //      e.g. Which one of these answers is correct?
+
+                       // To workaround this, we create new question names
+                       //      e.g. Which one of these answers is correct? (2)
+                       // until we get a question name for which there is no response yet on this attempt
+
+                       $loopcount++;
+                       $question->name = "$questionname ($loopcount)";
+
+                       // This method fails to correctly identify questions in
+                       // quizzes which allow questions to be shuffled or omitted.
+                       // As yet, there is no workaround for such cases.
+
+               } else {
+                       $response->question = $question->id;
+
+                       // add response record
+                       if(!$response->id = insert_record('hotpot_responses', $response)) {
+                               error("Could not add response record (attempt_id=$attempt->id, question_id=$question->id): ".$db->ErrorMsg(), $next_url);
+                       }
+
+                       // we can stop looping now
+                       $looping = false;
+               }
+       } // end while
+}
+function hotpot_adjust_response_field($quiztype, &$question, &$num, &$name, &$data) {
+       switch ($quiztype) {
+               case 'jbc':
+                       $question->type = HOTPOT_JCB;
+                       switch ($name) {
+                               case 'right':
+                                       $name = 'correct';
+                               break;
+                       }
+                       break;
+               case 'jcloze':
+                       $question->type = HOTPOT_JCLOZE;
+                       if (is_numeric($num)) {
+                               $question->name = $num;
+                       }
+                       switch ($name) {
+                               case 'penalties':
+                                       if (is_numeric($num)) {
+                                               $name = 'checks';
+                                               if (is_numeric($data)) {
+                                                       $data++;
+                                               }
+                                       }
+                                       break;
+                               case 'clue_shown':
+                                       $name = 'clues';
+                                       $data = ($data=='YES' ? 1 : 0);
+                                       break;
+                               case 'clue_text':
+                                       $name = 'text';
+                                       break;
+                       }
+                       break;
+               case 'jcross':
+                       $question->type = HOTPOT_JCROSS;
+                       $question->name = $num;
+                       switch ($name) {
+                               case '': // HotPot v2.0.x
+                                       $name = 'correct';
+                                       break;
+                               case 'clue':
+                                       $name = 'text';
+                                       break;
+                       }
+                       break;
+               case 'jmatch':
+                       $question->type = HOTPOT_JMATCH;
+                       switch ($name) {
+                               case 'attempts':
+                                       $name = 'penalties';
+                                       if (is_numeric($data) && $data>0) {
+                                               $data--;
+                                       }
+                               break;
+                               case 'lhs':
+                                       $name = 'name';
+                               break;
+                               case 'rhs':
+                                       $name = 'correct';
+                               break;
+                       }
+                       break;
+               case 'jmix':
+                       $question->type = HOTPOT_JMIX;
+                       $question->name = $num;
+                       switch ($name) {
+                               // keep these in for "restore" of courses
+                               // which were backed up with HotPot v2.0.x
+                               case 'wrongguesses':
+                                       $name = 'checks';
+                                       if (is_numeric($data)) {
+                                               $data++;
+                                       }
+                               break;
+                               case 'right':
+                                       $name = 'correct';
+                               break;
+                       }
+                       break;
+                       break;
+               case 'jquiz':
+                       switch ($name) {
+                               case 'type':
+                                       $data = HOTPOT_JQUIZ;
+                                       switch ($data) {
+                                               case 'multiple-choice':
+                                                       $data .= '.'.HOTPOT_JQUIZ_MULTICHOICE;
+                                               break;
+                                               case 'short-answer':
+                                                       $data .= '.'.HOTPOT_JQUIZ_SHORTANSWER;
+                                               break;
+                                               case 'hybrid':
+                                                       $data .= '.'.HOTPOT_JQUIZ_HYBRID;
+                                               break;
+                                               case 'multi-select':
+                                                       $data .= '.'.HOTPOT_JQUIZ_MULTISELECT;
+                                               case 'n/a':
+                                               default:
+                                                       // do nothing more
+                                               break;
+                                       }
+                               break;
+                               case 'question':
+                                       $name = 'name';
+                               break;
+                       }
+                       break;
+
+               case 'rhubarb':
+                       $question->type = HOTPOT_TEXTOYS_RHUBARB;
+                       if (empty($question->name)) {
+                               $question->name = $num;
+                       }
+                       break;
+
+               case 'sequitur':
+                       $question->type = HOTPOT_TEXTOYS_SEQUITUR;
+                       break;
+       }
+}
+function hotpot_string_ids($field_value) {
+       $ids = array();
+       $strings = explode(',', $field_value);
+       foreach($strings as $str) {
+               if ($id = hotpot_string_id($str)) {
+                       $ids[] = $id;
+               }
+       }
+       return implode(',', $ids);
+}
+function hotpot_string_id($str) {
+       $id = '';
+       if (isset($str) && $str<>'') {
+
+               // get the id from the table if it is already there
+               if (!$id = get_field('hotpot_strings', 'id', 'string', $str)) {
+
+                       // create a string record
+                       $record = NULL;
+                       $record->string = $str;
+
+                       // try and add the new string record
+                       if (!$id = insert_record('hotpot_strings', $record)) {
+                               global $db;
+                               error("Could not add string record for '".htmlspecialchars($str)."': ".$db->ErrorMsg());
+                       }
+               }
+       }
+       return $id;
+}
+
+if (!function_exists('file_get_contents')) {
+       // add this function for php version<4.3
+       function file_get_contents($filepath) {
+               $contents = file($filepath);
+               if (is_array($contents)) {
+                        $contents = implode('', $contents);
+               }
+               return $contents;
+       }
+}
+if (!function_exists('html_entity_decode')) {
+       // add this function for php version<4.3
+       function html_entity_decode($str) {
+               $t = get_html_translation_table(HTML_ENTITIES);
+               $t = array_flip($t);
+               return strtr($str, $t);
+       }
+
+}
+
+// required for Moodle 1.x
+if (!isset($CFG->pixpath)) {
+       $CFG->pixpath = "$CFG->wwwroot/pix";
+}
+
+if (!function_exists('fullname')) {
+       // add this function for Moodle 1.x
+       function fullname($user) {
+               return "$user->firstname $user->lastname";
+       }
+}
+if (!function_exists('get_user_preferences')) {
+       // add this function for Moodle 1.x
+       function get_user_preferences($name=NULL, $default=NULL, $userid=NULL) {
+               return $default;
+       }
+}
+if (!function_exists('set_user_preference')) {
+       // add this function for Moodle 1.x
+       function set_user_preference($name, $value, $otheruser=NULL) {
+               return false;
+       }
+}
+function hotpot_utf8_to_html_entity($char) {
+       // http://www.zend.com/codex.php?id=835&single=1
+
+       // array used to figure what number to decrement from character order value
+       // according to number of characters used to map unicode to ascii by utf-8
+       static $HOTPOT_UTF8_DECREMENT = array(
+               1=>0, 2=>192, 3=>224, 4=>240
+       );
+
+       // the number of bits to shift each character by
+       static $HOTPOT_UTF8_SHIFT = array(
+               1=>array(0=>0),
+               2=>array(0=>6,  1=>0),
+               3=>array(0=>12, 1=>6,  2=>0),
+               4=>array(0=>18, 1=>12, 2=>6, 3=>0)
+       );
+
+       $dec = 0;
+       $len = strlen($char);
+       for ($pos=0; $pos<$len; $pos++) {
+               $ord = ord ($char{$pos});
+               $ord -= ($pos ? 128 : $HOTPOT_UTF8_DECREMENT[$len]);
+               $dec += ($ord << $HOTPOT_UTF8_SHIFT[$len][$pos]);
+       }
+       return '&#x'.sprintf('%04X', $dec).';';
+}
+
+function hotpot_print_show_links($course, $location, $reference, $actions='', $spacer=' &nbsp; ', $new_window=false) {
+       global $CFG;
+       if (is_string($actions)) {
+               if (empty($actions)) {
+                       $actions = 'showxmlsource,showxmltree,showhtmlsource';
+               }
+               $actions = explode(',', $actions);
+       }
+       $strenterafilename = get_string('enterafilename', 'hotpot');
+       $html = <<<END_OF_SCRIPT
+<script type="text/javascript" language="javascript">
+<!--
+       function setLink(lnk) {
+               var form = document.forms['form'];
+               return setLinkAttribute(lnk, 'reference', form) && setLinkAttribute(lnk, 'location', form);
+       }
+       function setLinkAttribute(lnk, name, form) {
+               // set link attribute value using
+               // f(orm) name and e(lement) name
+
+               var r = true; // result
+
+               var obj = (form) ? form.elements[name] : null;
+               if (obj) {
+                       r = false;
+                       var v = getObjValue(obj);
+                       if (v=='') {
+                               alert('$strenterafilename');
+                       } else {
+                               var s = lnk.href;
+                               var i = s.indexOf('?');
+                               if (i>=0) {
+                                       i = s.indexOf(name+'=', i+1);
+                                       if (i>=0) {
+                                               i += name.length+1;
+                                               var ii = s.indexOf('&', i);
+                                               if (ii<0) {
+                                                       ii = s.length;
+                                               }
+                                               lnk.href = s.substring(0, i) + v + s.substring(ii);
+                                               r = true;
+                                       }
+                               }
+                       }
+               }
+               return r;
+       }
+       function getObjValue(obj) {
+               var v = ''; // the value
+               var t = (obj && obj.type) ? obj.type : "";
+               if (t=="text" || t=="textarea" || t=="hidden") {
+                       v = obj.value;
+               } else if (t=="select-one" || t=="select-multiple") {
+                       var l = obj.options.length;
+                       for (var i=0; i<l; i++) {
+                               if (obj.options[i].selected) {
+                                       v += (v=="" ? "" : ",") + obj.options[i].value;
+                               }
+                       }
+               }
+               return v;
+       }
+       function getDir(s) {
+               if (s.charAt(0)!='/') {
+                       s = '/' + s;
+               }
+               var i = s.lastIndexOf('/');
+               return s.substring(0, i);
+       }
+//-->
+</script>
+END_OF_SCRIPT;
+
+       foreach ($actions as $action) {
+               $html .= $spacer
+               .       '<a href="'
+               .                       $CFG->wwwroot.'/mod/hotpot/show.php'
+               .                       '?course='.$course.'&location='.$location.'&reference='.urlencode($reference).'&action='.$action
+               .               '"'
+               .               ' onclick="return setLink(this);"'
+               .               ($new_window ? ' target="_blank"' : '')
+               .       '>'.get_string($action, 'hotpot').'</a>'
+               ;
+       }
+       print '<span class="helplink">'.$html.'</span>';
+}
+
+?>