* @package questionbank
* @subpackage importexport
*/
+require_once($CFG->dirroot . '/mod/hotpot/lib.php');
class qformat_hotpot extends qformat_default {
return $str;
}
} // end class
-
-// get the standard XML parser supplied with Moodle
-require_once("$CFG->libdir/xmlize.php");
-
-class hotpot_xml_tree {
- function hotpot_xml_tree($str, $xml_root='') {
- if (empty($str)) {
- $this->xml = array();
- } else {
- // encode htmlentities in JCloze
- $this->encode_cdata($str, 'gap-fill');
- // xmlize (=convert xml to tree)
- $this->xml = xmlize($str, 0);
- }
- $this->xml_root = $xml_root;
- }
- function xml_value($tags, $more_tags="[0]['#']") {
-
- $value = null;
- if (isset($this->xml) && is_array($this->xml)) {
-
- $all_tags = $this->xml_root;
- if ($tags) {
- $all_tags .= "['".str_replace(",", "'][0]['#']['", $tags)."']";
- }
- $all_tags .= $more_tags;
-
- $pos = strrpos($all_tags, '[');
- if ($pos===false) {
- $most_tags = ''; // shouldn't happen !!
- } else {
- $most_tags = substr($all_tags, 0, $pos);
- }
-
- eval('if (isset($this->xml'.$most_tags.') && is_array($this->xml'.$most_tags.') && isset($this->xml'.$all_tags.')) {'
- .'$value = &$this->xml'.$all_tags.';'
- .'} else {'
- .'$value = null;'
- .'}'
- );
- }
-
- if (is_string($value)) {
-
- // decode angle brackets and ampersands
- $value = strtr($value, array('<'=>'<', '>'=>'>', '&'=>'&'));
-
- // 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'
- . ')'
- ;
- $search = '#(<'.$htmltags.'[^>]*'.'>)\s+'.'(?='.'<'.')#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)
-
- // multibyte unicode characters can be detected by checking the hex value of the first 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 encode_cdata(&$str, $tag) {
-
- // conversion tables
- static $HTML_ENTITIES = array(
- ''' => "'",
- '"' => '"',
- '<' => '<',
- '>' => '>',
- '&' => '&',
- );
- static $ILLEGAL_STRINGS = array(
- "\r" => '',
- "\n" => '<br />',
- ']]>' => ']]>',
- );
-
- // 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];
- }
- }
-}
-
-function hotpot_charcode_to_utf8($charcode) {
- if ($charcode <= 0x7F) {
- // ascii char (roman alphabet + punctuation)
- return chr($charcode);
- }
- if ($charcode <= 0x7FF) {
- // 2-byte char
- return chr(($charcode >> 0x06) + 0xC0).chr(($charcode & 0x3F) + 128);
- }
- if ($charcode <= 0xFFFF) {
- // 3-byte char
- return chr(($charcode >> 0x0C) + 0xE0).chr((($charcode >> 0x06) & 0x3F) + 0x80).chr(($charcode & 0x3F) + 0x80);
- }
- if ($charcode <= 0x1FFFFF) {
- // 4-byte char
- return chr(($charcode >> 0x12) + 0xF0).chr((($charcode >> 0x0C) & 0x3F) + 0x80).chr((($charcode >> 0x06) & 0x3F) + 0x80).chr(($charcode & 0x3F) + 0x80);
- }
- // unidentified char code !!
- return ' ';
-}
-
-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_convert_relative_urls($str, $baseurl, $filename) {
- $tagopen = '(?:(<)|(<)|(&#x003C;))'; // left angle bracket
- $tagclose = '(?(2)>|(?(3)>|(?(4)&#x003E;)))'; // right angle bracket (to match left angle bracket)
-
- $space = '\s+'; // at least one space
- $anychar = '(?:[^>]*?)'; // any character
-
- $quoteopen = '("|"|&quot;)'; // open quote
- $quoteclose = '\\5'; // close quote (to match open quote)
-
- $replace = "hotpot_convert_relative_url('".$baseurl."', '".$filename."', '\\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);
- }
-
- return $str;
-}
-
-function hotpot_convert_relative_url($baseurl, $filename, $opentag, $url, $closetag, $depricated=true) {
- // catch <PARAM name="FlashVars" value="TheSound=soundfile.mp3">
- // ampersands can appear as "&", "&" or "&#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];
-
- // there appears to be no query or fragment in this url
- } else {
- $query = '';
- $fragment = '';
- }
-
- if ($url) {
- $url = hotpot_convert_url($baseurl, $filename, $url, false);
- }
-
- if ($query) {
- $search = '#'.'(file|src|thesound)='."([^&]+)".'#ise';
- $replace = "'\\1='.hotpot_convert_url('".$baseurl."','".$filename."','\\2')";
- $query = preg_replace($search, $replace, $query);
- }
-
- $url = $opentag.$url.$query.$fragment.$closetag;
-
- return $url;
-}
-
-function hotpot_convert_url($baseurl, $filename, $url, $depricated=true) {
- // maintain a cache of converted urls
- static $HOTPOT_RELATIVE_URLS = array();
-
- // 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 $filename
- $dir = dirname($filename);
-
- // 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;
-}
-
-// allow importing in Moodle v1.4 (and less)
-// same core functions but different class name
-if (!class_exists("quiz_file_format")) {
- class quiz_file_format extends qformat_default {
- function readquestions ($lines) {
- $format = new qformat_hotpot();
- return $format->readquestions($lines);
- }
- }
-}
-
-?>