Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'w43_MDL-29921_m22_qti2' of git://github.com/skodak/moodle

  • Loading branch information...
commit c84f4700ec3ba6207ead924231c5ca4b918d14d7 2 parents ae44df3 + 2dc5461
Aparup Banerjee authored October 31, 2011
2  lib/pluginlib.php
@@ -385,7 +385,7 @@ public static function standard_plugins_list($type) {
385 385
 
386 386
             'qformat' => array(
387 387
                 'aiken', 'blackboard', 'blackboard_six', 'examview', 'gift',
388  
-                'learnwise', 'missingword', 'multianswer', 'qti_two', 'webct',
  388
+                'learnwise', 'missingword', 'multianswer', 'webct',
389 389
                 'xhtml', 'xml'
390 390
             ),
391 391
 
1  mod/quiz/lang/en/quiz.php
@@ -514,7 +514,6 @@
514 514
 $string['qname'] = 'name';
515 515
 $string['qbrief'] = 'Q. {$a}';
516 516
 $string['qti'] = 'IMS QTI format';
517  
-$string['qti_two'] = 'IMS QTI 2.0 format';
518 517
 $string['qtypename'] = 'type, name';
519 518
 $string['question'] = 'Question';
520 519
 $string['questionbankcontents'] = 'Question bank contents';
169  question/format/qti_two/custommediafilter.php
... ...
@@ -1,169 +0,0 @@
1  
-<?php
2  
-// This file is part of Moodle - http://moodle.org/
3  
-//
4  
-// Moodle is free software: you can redistribute it and/or modify
5  
-// it under the terms of the GNU General Public License as published by
6  
-// the Free Software Foundation, either version 3 of the License, or
7  
-// (at your option) any later version.
8  
-//
9  
-// Moodle is distributed in the hope that it will be useful,
10  
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11  
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  
-// GNU General Public License for more details.
13  
-//
14  
-// You should have received a copy of the GNU General Public License
15  
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16  
-
17  
-/**
18  
- * Modified from the original filter/mediaplugin/filter.php
19  
- *
20  
- * @package    qformat
21  
- * @subpackage qti_two
22  
- * @copyright  2005 brian@mediagonal.ch
23  
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  
- */
25  
-
26  
-
27  
-defined('MOODLE_INTERNAL') || die();
28  
-
29  
-
30  
-/**
31  
- * Modified from the original filter/mediaplugin/filter.php
32  
- */
33  
-function custom_mediaplugin_filter($text, $width = null, $height = null) {
34  
-    global $CFG;
35  
-    if (is_null($width) || $width == 0) {
36  
-        $usedefaults = true;
37  
-        $width = 400;
38  
-        $height = 300;
39  
-    } else {
40  
-        $usedefaults = false;
41  
-    }
42  
-
43  
-    if (empty($CFG->filter_mediaplugin_ignore_mp3)) {
44  
-        $search = '/<a(.*?)href=\"([^<]+)\.mp3\"([^>]*)>(.*?)<\/a>/i';
45  
-
46  
-        $replace  = '\\0&nbsp;<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';
47  
-        $replace .= ' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" ';
48  
-        $replace .= ' width="35" height="18" id="mp3player" align="">';
49  
-        $replace .= " <param name=movie value=\"$CFG->wwwroot/filter/mediaplugin/mp3player.swf?src=\\2.mp3\">";
50  
-        $replace .= ' <param name=quality value=high>';
51  
-        $replace .= ' <param name=bgcolor value="#333333">';
52  
-        $replace .= " <embed src=\"$CFG->wwwroot/filter/mediaplugin/mp3player.swf?src=\\2.mp3\" ";
53  
-        $replace .= "  quality=high bgcolor=\"#333333\" width=\"35\" height=\"18\" name=\"mp3player\" ";
54  
-        $replace .= ' type="application/x-shockwave-flash" ';
55  
-        $replace .= ' pluginspage="http://www.macromedia.com/go/getflashplayer">';
56  
-        $replace .= '</embed>';
57  
-        $replace .= '</object>&nbsp;';
58  
-
59  
-        $text = preg_replace($search, $replace, $text);
60  
-    }
61  
-
62  
-    if (empty($CFG->filter_mediaplugin_ignore_swf)) {
63  
-        $search = '/<a(.*?)href=\"([^<]+)\.swf\"([^>]*)>(.*?)<\/a>/i';
64  
-        $replace  = '\\0<object '.
65  
-                            'type="application/x-shockwave-flash" ' .
66  
-                            'data="\\2.swf" ' .
67  
-                            'width="' . $width . '" ' .
68  
-                            'height="' . $height . '"> ' .
69  
-                        '<param name="movie" value="\\2.swf" /> ' .
70  
-                        '<param name="wmode" value="transparent" />' .
71  
-                        '</object>';
72  
-
73  
-
74  
-/*        $replace  = '\\0<p class="mediaplugin"><object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';
75  
-        $replace .= ' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" ';
76  
-        $replace .= ' width="' . $width . '" height="' . $height . '" id="mp3player" align="">';
77  
-        $replace .= " <param name=movie value=\"\\2.swf\">";
78  
-        $replace .= ' <param name=quality value=high>';
79  
-        $replace .= " <embed src=\"\\2.swf\" ";
80  
-        $replace .= "  quality=high width=\"$width\" height=\"$height\" name=\"flashfilter\" ";
81  
-        $replace .= ' type="application/x-shockwave-flash" ';
82  
-        $replace .= ' pluginspage="http://www.macromedia.com/go/getflashplayer">';
83  
-        $replace .= '</embed>';
84  
-        $replace .= '</object></p>';*/
85  
-
86  
-        $text = preg_replace($search, $replace, $text);
87  
-    }
88  
-
89  
-    if (empty($CFG->filter_mediaplugin_ignore_mov)) {
90  
-        $search = '/<a(.*?)href=\"([^<]+)\.mov\"([^>]*)>(.*?)<\/a>/i';
91  
-
92  
-        $replace  = '\\0<p class="mediaplugin"><object classid="CLSID:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"';
93  
-        $replace .= '        codebase="http://www.apple.com/qtactivex/qtplugin.cab" ';
94  
-        $replace .= '        height="' . $height . '" width="' . $width . '"';
95  
-        $replace .= '        id="quicktime" align="" type="application/x-oleobject">';
96  
-        $replace .= "<param name=\"src\" value=\"\\2.mov\" />";
97  
-        $replace .= '<param name="autoplay" value=false />';
98  
-        $replace .= '<param name="loop" value=true />';
99  
-        $replace .= '<param name="controller" value=true />';
100  
-        $replace .= '<param name="scale" value="aspect" />';
101  
-        $replace .= "\n<embed src=\"\\2.mov\" name=\"quicktime\" type=\"video/quicktime\" ";
102  
-        $replace .= ' height="' . $height . '" width="' . $width . '" scale="aspect" ';
103  
-        $replace .= ' autoplay="false" controller="true" loop="true" ';
104  
-        $replace .= ' pluginspage="http://quicktime.apple.com/">';
105  
-        $replace .= '</embed>';
106  
-        $replace .= '</object>&nbsp;';
107  
-
108  
-        $text = preg_replace($search, $replace, $text);
109  
-    }
110  
-
111  
-    if (empty($CFG->filter_mediaplugin_ignore_wmv)) {
112  
-        $search = '/<a(.*?)href=\"([^<]+)\.wmv\"([^>]*)>(.*?)<\/a>/i';
113  
-
114  
-        $replace  = '\\0<p class="mediaplugin"><object classid="CLSID:22D6f312-B0F6-11D0-94AB-0080C74C7E95"';
115  
-        $replace .= ' codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701" ';
116  
-        $replace .= ' standby="Loading Microsoft? Windows? Media Player components..." ';
117  
-        $replace .= ' id="msplayer" align="" type="application/x-oleobject">';
118  
-        $replace .= "<param name=\"Filename\" value=\"\\2.wmv\">";
119  
-        $replace .= '<param name="ShowControls" value=true />';
120  
-        $replace .= '<param name="AutoRewind" value=true />';
121  
-        $replace .= '<param name="AutoStart" value=false />';
122  
-        $replace .= '<param name="Autosize" value=true />';
123  
-        $replace .= '<param name="EnableContextMenu" value=true />';
124  
-        $replace .= '<param name="TransparentAtStart" value=false />';
125  
-        $replace .= '<param name="AnimationAtStart" value=false />';
126  
-        $replace .= '<param name="ShowGotoBar" value=false />';
127  
-        $replace .= '<param name="EnableFullScreenControls" value=true />';
128  
-        $replace .= "\n<embed src=\"\\2.wmv\" name=\"msplayer\" type=\"video/x-ms\" ";
129  
-        $replace .= ' ShowControls="1" AutoRewind="1" AutoStart="0" Autosize="0" EnableContextMenu="1"';
130  
-        $replace .= ' TransparentAtStart="0" AnimationAtStart="0" ShowGotoBar="0" EnableFullScreenControls="1"';
131  
-        $replace .= ' pluginspage="http://www.microsoft.com/Windows/Downloads/Contents/Products/MediaPlayer/">';
132  
-        $replace .= '</embed>';
133  
-        $replace .= '</object>&nbsp;';
134  
-
135  
-        $text = preg_replace($search, $replace, $text);
136  
-    }
137  
-
138  
-    if ($usedefaults) {
139  
-        $width = 240;
140  
-        $height = 180;
141  
-    }
142  
-
143  
-    if (empty($CFG->filter_mediaplugin_ignore_mpg)) {
144  
-        $search = '/<a(.*?)href=\"([^<]+)\.(mpe?g)\"([^>]*)>(.*?)<\/a>/i';
145  
-
146  
-        $replace = '\\0<p class="mediaplugin"><object width="' . $width . '" height="' . $height . '">';
147  
-        $replace .= '<param name="src" value="\\2.\\3">';
148  
-        $replace .= '<param name="controller" value="true">';
149  
-        $replace .= '<param name="autoplay" value="false">';
150  
-        $replace .= '<embed src="\\2.\\3" width="' . $width . '" height="' . $height . '" controller="true" autoplay="false"> </embed>';
151  
-        $replace .= '</object></p>';
152  
-
153  
-        $text = preg_replace($search, $replace, $text);
154  
-    }
155  
-
156  
-    if (empty($CFG->filter_mediaplugin_ignore_avi)) {
157  
-        $search = '/<a(.*?)href=\"([^<]+)\.avi\"([^>]*)>(.*?)<\/a>/i';
158  
-
159  
-        $replace = '\\0<p class="mediaplugin"><object width="' . $width . '" height="' . $height . '">';
160  
-        $replace .= '<param name="src" value="\\2.avi">';
161  
-        $replace .= '<param name="controller" value="true">';
162  
-        $replace .= '<param name="autoplay" value="false">';
163  
-        $replace .= '<embed src="\\2.avi" width="' . $width . '" height="' . $height . '" controller="true" autoplay="false"> </embed>';
164  
-        $replace .= '</object>&nbsp;';
165  
-
166  
-        $text = preg_replace($search, $replace, $text);
167  
-    }
168  
-    return $text;
169  
-}
940  question/format/qti_two/format.php
... ...
@@ -1,940 +0,0 @@
1  
-<?php
2  
-// This file is part of Moodle - http://moodle.org/
3  
-//
4  
-// Moodle is free software: you can redistribute it and/or modify
5  
-// it under the terms of the GNU General Public License as published by
6  
-// the Free Software Foundation, either version 3 of the License, or
7  
-// (at your option) any later version.
8  
-//
9  
-// Moodle is distributed in the hope that it will be useful,
10  
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11  
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  
-// GNU General Public License for more details.
13  
-//
14  
-// You should have received a copy of the GNU General Public License
15  
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16  
-
17  
-/**
18  
- * Attempt at a QTI 2 question exporter.
19  
- *
20  
- * @package    qformat
21  
- * @subpackage qti_two
22  
- * @copyright  2005 brian@mediagonal.ch
23  
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  
- */
25  
-
26  
-
27  
-defined('MOODLE_INTERNAL') || die();
28  
-
29  
-require_once("$CFG->dirroot/question/format/qti_two/qt_common.php");
30  
-
31  
-define('CLOZE_TRAILING_TEXT_ID', 9999999);
32  
-
33  
-
34  
-/**
35  
- * Attempt at a QTI 2 question exporter.
36  
- *
37  
- * Sadly, not very well maintained.
38  
- *
39  
- * @copyright  2005 brian@mediagonal.ch
40  
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  
- */
42  
-class qformat_qti_two extends qformat_default {
43  
-
44  
-    var $lang;
45  
-
46  
-    function provide_export() {
47  
-       return true;
48  
-    }
49  
-
50  
-    function indent_xhtml($source, $indenter = ' ') {
51  
-        // xml tidier-upper
52  
-        // (c) Ari Koivula http://ventionline.com
53  
-
54  
-        // Remove all pre-existing formatting.
55  
-        // Remove all newlines.
56  
-        $source = str_replace("\n", '', $source);
57  
-        $source = str_replace("\r", '', $source);
58  
-        // Remove all tabs.
59  
-        $source = str_replace("\t", '', $source);
60  
-        // Remove all space after ">" and before "<".
61  
-        $source = preg_replace("/>( )*", ">/", $source);
62  
-        $source = preg_replace("/( )*<", "</", $source);
63  
-
64  
-        // Iterate through the source.
65  
-        $level = 0;
66  
-        $source_len = strlen($source);
67  
-        $pt = 0;
68  
-        while ($pt < $source_len) {
69  
-            if ($source{$pt} === '<') {
70  
-                // We have entered a tag.
71  
-                // Remember the point where the tag starts.
72  
-                $started_at = $pt;
73  
-                $tag_level = 1;
74  
-                // If the second letter of the tag is "/", assume its an ending tag.
75  
-                if ($source{$pt+1} === '/') {
76  
-                    $tag_level = -1;
77  
-                }
78  
-                // If the second letter of the tag is "!", assume its an "invisible" tag.
79  
-                if ($source{$pt+1} === '!') {
80  
-                    $tag_level = 0;
81  
-                }
82  
-                // Iterate throught the source until the end of tag.
83  
-                while ($source{$pt} !== '>') {
84  
-                    $pt++;
85  
-                }
86  
-                // If the second last letter is "/", assume its a self ending tag.
87  
-                if ($source{$pt-1} === '/') {
88  
-                    $tag_level = 0;
89  
-                }
90  
-                $tag_lenght = $pt+1-$started_at;
91  
-
92  
-                // Decide the level of indention for this tag.
93  
-                // If this was an ending tag, decrease indent level for this tag..
94  
-                if ($tag_level === -1) {
95  
-                    $level--;
96  
-                }
97  
-                // Place the tag in an array with proper indention.
98  
-                $array[] = str_repeat($indenter, $level).substr($source, $started_at, $tag_lenght);
99  
-                // If this was a starting tag, increase the indent level after this tag.
100  
-                if ($tag_level === 1) {
101  
-                    $level++;
102  
-                }
103  
-                // if it was a self closing tag, dont do shit.
104  
-            }
105  
-            // Were out of the tag.
106  
-            // If next letter exists...
107  
-            if (($pt+1) < $source_len) {
108  
-                // ... and its not an "<".
109  
-                if ($source{$pt+1} !== '<') {
110  
-                    $started_at = $pt+1;
111  
-                    // Iterate through the source until the start of new tag or until we reach the end of file.
112  
-                    while ($source{$pt} !== '<' && $pt < $source_len) {
113  
-                        $pt++;
114  
-                    }
115  
-                    // If we found a "<" (we didnt find the end of file)
116  
-                    if ($source{$pt} === '<') {
117  
-                        $tag_lenght = $pt-$started_at;
118  
-                        // Place the stuff in an array with proper indention.
119  
-                        $array[] = str_repeat($indenter, $level).substr($source, $started_at, $tag_lenght);
120  
-                    }
121  
-                // If the next tag is "<", just advance pointer and let the tag indenter take care of it.
122  
-                } else {
123  
-                    $pt++;
124  
-                }
125  
-            // If the next letter doesnt exist... Were done... well, almost..
126  
-            } else {
127  
-                break;
128  
-            }
129  
-        }
130  
-        // Replace old source with the new one we just collected into our array.
131  
-        $source = implode($array, "\n");
132  
-        return $source;
133  
-    }
134  
-
135  
-    function importpreprocess() {
136  
-        global $CFG;
137  
-
138  
-        print_error('cannotimportformat', 'question');
139  
-    }
140  
-
141  
-    public function exportpreprocess() {
142  
-        global $CFG;
143  
-
144  
-        require_once("{$CFG->libdir}/smarty/Smarty.class.php");
145  
-
146  
-        // assign the language for the export: by parameter, SESSION, USER, or the default of 'en'
147  
-        $lang = current_language();
148  
-        $this->lang = $lang;
149  
-
150  
-        return parent::exportpreprocess();
151  
-    }
152  
-
153  
-
154  
-    function export_file_extension() {
155  
-        // override default type so extension is .xml
156  
-
157  
-        return ".zip";
158  
-    }
159  
-
160  
-    function get_qtype( $type_id ) {
161  
-        // translates question type code number into actual name
162  
-
163  
-        switch( $type_id ) {
164  
-        case TRUEFALSE:
165  
-            $name = 'truefalse';
166  
-            break;
167  
-        case MULTICHOICE:
168  
-            $name = 'multichoice';
169  
-            break;
170  
-        case SHORTANSWER:
171  
-            $name = 'shortanswer';
172  
-            break;
173  
-        case NUMERICAL:
174  
-            $name = 'numerical';
175  
-            break;
176  
-        case MATCH:
177  
-            $name = 'matching';
178  
-            break;
179  
-        case DESCRIPTION:
180  
-            $name = 'description';
181  
-            break;
182  
-        case MULTIANSWER:
183  
-            $name = 'multianswer';
184  
-            break;
185  
-        default:
186  
-            $name = 'Unknown';
187  
-        }
188  
-        return $name;
189  
-    }
190  
-
191  
-    function writetext( $raw ) {
192  
-        // generates <text></text> tags, processing raw text therein
193  
-
194  
-        // for now, don't allow any additional tags in text
195  
-        // otherwise xml rules would probably get broken
196  
-        $raw = strip_tags( $raw );
197  
-
198  
-        return "<text>$raw</text>\n";
199  
-    }
200  
-
201  
-
202  
-/**
203  
- * flattens $object['media'], copies $object['media'] to $path, and sets $object['mediamimetype']
204  
- *
205  
- * @param array &$object containing a field 'media'
206  
- * @param string $path the full path name to where the media files need to be copied
207  
- * @param int $courseid
208  
- * @return: mixed - true on success or in case of an empty media field, an error string if the file copy fails
209  
- */
210  
-function copy_and_flatten(&$object, $path, $courseid) {
211  
-    global $CFG;
212  
-    if (!empty($object['media'])) {
213  
-        $location = $object['media'];
214  
-        $object['media'] = $this->flatten_image_name($location);
215  
-        if (!@copy("{$CFG->dataroot}/$courseid/$location", "$path/{$object['media']}")) {
216  
-            return "Failed to copy {$CFG->dataroot}/$courseid/$location to $path/{$object['media']}";
217  
-        }
218  
-        if (empty($object['mediamimetype'])) {
219  
-            $object['mediamimetype'] = mimeinfo('type', $object['media']);
220  
-        }
221  
-    }
222  
-    return true;
223  
-}
224  
-/**
225  
- * copies all files needed by the questions to the given $path, and flattens the file names
226  
- *
227  
- * @param array $questions the question objects
228  
- * @param string $path the full path name to where the media files need to be copied
229  
- * @param int $courseid
230  
- * @return mixed true on success, an array of error messages otherwise
231  
- */
232  
-function handle_questions_media(&$questions, $path, $courseid) {
233  
-    global $CFG;
234  
-    $errors = array();
235  
-    foreach ($questions as $key=>$question) {
236  
-
237  
-    // todo: handle in-line media (specified in the question text)
238  
-        if (!empty($question->image)) {
239  
-            $location = $questions[$key]->image;
240  
-            $questions[$key]->mediaurl = $this->flatten_image_name($location);
241  
-            if (!@copy("{$CFG->dataroot}/$courseid/$location", "$path/{$questions[$key]->mediaurl}")) {
242  
-                $errors[] = "Failed to copy {$CFG->dataroot}/$courseid/$location to $path/{$questions[$key]->mediaurl}";
243  
-            }
244  
-            if (empty($question->mediamimetype)) {
245  
-                $questions[$key]->mediamimetype = mimeinfo('type', $question->image);
246  
-            }
247  
-        }
248  
-    }
249  
-
250  
-    return empty($errors) ? true : $errors;
251  
-}
252  
-
253  
-    /**
254  
-     * exports the questions in a question category to the given location
255  
-     *
256  
-     * The parent class method was overridden because the IMS export consists of multiple files
257  
-     *
258  
-     * @param string $filename the directory name which will hold the exported files
259  
-     * @return bool - or errors out
260  
-     */
261  
-    public function exportprocess() {
262  
-
263  
-        global $CFG, $OUTPUT, $USER;
264  
-        $courseid = $this->course->id;
265  
-
266  
-        $path = 'qformat_qti_two/' . $USER->id . '/' . $this->filename;
267  
-        // create a directory for the exports (if not already existing)
268  
-        if (!make_temp_directory($path)) {
269  
-              throw new moodle_exception('cannotcreatepath', 'question', '', $path);
270  
-        }
271  
-        $path = $CFG->dataroot . '/' . $path;
272  
-
273  
-        // get the questions (from database) in this category
274  
-        $questions = get_questions_category( $this->category );
275  
-
276  
-        // create the imsmanifest file
277  
-        $smarty =& $this->init_smarty();
278  
-        $this->add_qti_info($questions);
279  
-        // copy files used by the main questions to the export directory
280  
-        $result = $this->handle_questions_media($questions, $path, $courseid);
281  
-        if ($result !== true) {
282  
-            throw new coding_exception(implode("<br />", $result));
283  
-        }
284  
-
285  
-        $manifestquestions = $this->objects_to_array($questions);
286  
-        $manifestid = str_replace(array(':', '/'), array('-','_'), "question_category_{$this->category->id}---{$CFG->wwwroot}");
287  
-        $smarty->assign('externalfiles', 1);
288  
-        $smarty->assign('manifestidentifier', $manifestid);
289  
-        $smarty->assign('quiztitle', "question_category_{$this->category->id}");
290  
-        $smarty->assign('quizinfo', "All questions in category {$this->category->id}");
291  
-        $smarty->assign('questions', $manifestquestions);
292  
-        $smarty->assign('lang', $this->lang);
293  
-        $smarty->error_reporting = 99;
294  
-        $expout = $smarty->fetch('imsmanifest.tpl');
295  
-        $filepath = $path.'/imsmanifest.xml';
296  
-        if (empty($expout)) {
297  
-            print_error('emptyxml', 'question');
298  
-        }
299  
-        if (!$fh=fopen($filepath,"w")) {
300  
-            print_error('cannotopenforwriting', 'question', '', $filepath);
301  
-        }
302  
-        if (!fwrite($fh, $expout)) {
303  
-            print_error('cannotwriteto', 'question', '', $filepath);
304  
-        }
305  
-        fclose($fh);
306  
-
307  
-        // iterate through questions
308  
-        foreach($questions as $question) {
309  
-
310  
-            // results are first written into string (and then to a file)
311  
-            $expout = $this->writequestion( $question , null, true, $path) . "\n";
312  
-            $expout = $this->presave_process( $expout );
313  
-
314  
-            $filepath = $path.'/'.$this->get_assesment_item_id($question) . ".xml";
315  
-            if (!$fh=fopen($filepath,"w")) {
316  
-                print_error('cannotopenforwriting', 'question', '', $filepath);
317  
-            }
318  
-            if (!fwrite($fh, $expout)) {
319  
-                print_error('cannotwriteto', 'question', '', $filepath);
320  
-            }
321  
-            fclose($fh);
322  
-
323  
-        }
324  
-
325  
-        // zip files into single export file
326  
-        zip_files( array($path), "$path.zip" );
327  
-
328  
-        // remove the temporary directory
329  
-        remove_dir( $path );
330  
-
331  
-        return true;
332  
-    }
333  
-
334  
-/**
335  
- * exports a quiz (as opposed to exporting a category of questions)
336  
- *
337  
- * The parent class method was overridden because the IMS export consists of multiple files
338  
- *
339  
- * @param object $quiz
340  
- * @param array $questions - an array of question objects
341  
- * @param object $result - if set, contains result of calling quiz_grade_responses()
342  
- * @param string $redirect - a URL to redirect to in case of failure
343  
- * @param string $submiturl - the URL for the qti player to send the results to (e.g. attempt.php)
344  
- * @todo use $result in the ouput
345  
- */
346  
-     function export_quiz($course, $quiz, $questions, $result, $redirect, $submiturl = null) {
347  
-        $this->xml_entitize($course);
348  
-        $this->xml_entitize($quiz);
349  
-        $this->xml_entitize($questions);
350  
-        $this->xml_entitize($result);
351  
-        $this->xml_entitize($submiturl);
352  
-        if (!$this->exportpreprocess(0, $course)) { // Do anything before that we need to
353  
-            print_error('errorpreprocess', 'question', $redirect);
354  
-        }
355  
-        if (!$this->exportprocess_quiz($quiz, $questions, $result, $submiturl, $course)) {         // Process the export data
356  
-            print_error('errorprocess','question', $redirect);
357  
-        }
358  
-        if (!$this->exportpostprocess()) { // In case anything needs to be done after
359  
-            print_error('errorpostprocess', 'question', $redirect);
360  
-        }
361  
-
362  
-    }
363  
-
364  
-
365  
-/**
366  
- * This function is called to export a quiz (as opposed to exporting a category of questions)
367  
- *
368  
- * @uses $USER
369  
- * @param object $quiz
370  
- * @param array $questions - an array of question objects
371  
- * @param object $result - if set, contains result of calling quiz_grade_responses()
372  
- * @todo use $result in the ouput
373  
- */
374  
-    function exportprocess_quiz($quiz, $questions, $result, $submiturl, $course) {
375  
-        global $USER;
376  
-        global $CFG;
377  
-
378  
-        $gradingmethod = array(
379  
-            1 => 'GRADEHIGHEST',
380  
-            2 => 'GRADEAVERAGE',
381  
-            3 => 'ATTEMPTFIRST',
382  
-            4 => 'ATTEMPTLAST'
383  
-        );
384  
-
385  
-        $questions = $this->quiz_export_prepare_questions($questions, $quiz->id, $course->id, $quiz->shuffleanswers);
386  
-
387  
-        $smarty = $this->init_smarty();
388  
-        $smarty->assign('questions', $questions);
389  
-
390  
-        // quiz level smarty variables
391  
-        $manifestid = str_replace(array(':', '/'), array('-','_'), "quiz{$quiz->id}-{$CFG->wwwroot}");
392  
-        $smarty->assign('manifestidentifier', $manifestid);
393  
-        $smarty->assign('submiturl', $submiturl);
394  
-        $smarty->assign('userid', $USER->id);
395  
-        $smarty->assign('username', htmlspecialchars($USER->username, ENT_COMPAT, 'UTF-8'));
396  
-        $smarty->assign('quiz_level_export', 1);
397  
-        $smarty->assign('quiztitle', format_string($quiz->name,true)); //assigned specifically so as not to cause problems with category-level export
398  
-        $smarty->assign('quiztimeopen', date('Y-m-d\TH:i:s', $quiz->timeopen)); // ditto
399  
-        $smarty->assign('quiztimeclose', date('Y-m-d\TH:i:s', $quiz->timeclose)); // ditto
400  
-        $smarty->assign('grademethod', $gradingmethod[$quiz->grademethod]);
401  
-        $smarty->assign('quiz', $quiz);
402  
-        $smarty->assign('course', $course);
403  
-        $smarty->assign('lang', $this->lang);
404  
-        $expout = $smarty->fetch('imsmanifest.tpl');
405  
-        echo $expout;
406  
-        return true;
407  
-    }
408  
-
409  
-    /**
410  
-     * Prepares questions for quiz export
411  
-     *
412  
-     * The questions are changed as follows:
413  
-     *   - the question answers atached to the questions
414  
-     *   - image set to an http reference instead of a file path
415  
-     *   - qti specific info added
416  
-     *   - exporttext added, which contains an xml-formatted qti assesmentItem
417  
-     *
418  
-     * @param array $questions - an array of question objects
419  
-     * @param int $quizid
420  
-     * @return an array of question arrays
421  
-     */
422  
-    function quiz_export_prepare_questions($questions, $quizid, $courseid, $shuffleanswers = null) {
423  
-        global $CFG;
424  
-        // add the answers to the questions and format the image property
425  
-        foreach ($questions as $key=>$question) {
426  
-            $questions[$key] = get_question_data($question);
427  
-            $questions[$key]->courseid = $courseid;
428  
-            $questions[$key]->quizid = $quizid;
429  
-
430  
-            if ($question->image) {
431  
-
432  
-                if (empty($question->mediamimetype)) {
433  
-                  $questions[$key]->mediamimetype = mimeinfo('type',$question->image);
434  
-                }
435  
-
436  
-                $localfile = (substr(strtolower($question->image), 0, 7) == 'http://') ? false : true;
437  
-
438  
-                if ($localfile) {
439  
-                    // create the http url that the player will need to access the file
440  
-                    if ($CFG->slasharguments) {        // Use this method if possible for better caching
441  
-                        $questions[$key]->mediaurl = "$CFG->wwwroot/file.php/$question->image";
442  
-                    } else {
443  
-                        $questions[$key]->mediaurl = "$CFG->wwwroot/file.php?file=$question->image";
444  
-                    }
445  
-                } else {
446  
-                    $questions[$key]->mediaurl = $question->image;
447  
-                }
448  
-            }
449  
-        }
450  
-
451  
-        $this->add_qti_info($questions);
452  
-        $questions = $this->questions_with_export_info($questions, $shuffleanswers);
453  
-        $questions = $this->objects_to_array($questions);
454  
-        return $questions;
455  
-    }
456  
-
457  
-/**
458  
- * calls htmlspecialchars for each string field, to convert, for example, & to &amp;
459  
- *
460  
- * collections are processed recursively
461  
- *
462  
- * @param array $collection - an array or object or string
463  
- */
464  
-function xml_entitize(&$collection) {
465  
-    if (is_array($collection)) {
466  
-        foreach ($collection as $key=>$var) {
467  
-            if (is_string($var)) {
468  
-                $collection[$key]= htmlspecialchars($var, ENT_COMPAT, 'UTF-8');
469  
-            } else if (is_array($var) || is_object($var)) {
470  
-                $this->xml_entitize($collection[$key]);
471  
-            }
472  
-        }
473  
-    } else if (is_object($collection)) {
474  
-        $vars = get_object_vars($collection);
475  
-        foreach ($vars as $key=>$var) {
476  
-            if (is_string($var)) {
477  
-                $collection->$key = htmlspecialchars($var, ENT_COMPAT, 'UTF-8');
478  
-            } else if (is_array($var) || is_object($var)) {
479  
-                $this->xml_entitize($collection->$key);
480  
-            }
481  
-        }
482  
-    } else if (is_string($collection)) {
483  
-        $collection = htmlspecialchars($collection, ENT_COMPAT, 'UTF-8');
484  
-    }
485  
-}
486  
-
487  
-/**
488  
- * adds exporttext property to the questions
489  
- *
490  
- * Adds the qti export text to the questions
491  
- *
492  
- * @param array $questions - an array of question objects
493  
- * @return an array of question objects
494  
- */
495  
-    function questions_with_export_info($questions, $shuffleanswers = null) {
496  
-        $exportquestions = array();
497  
-        foreach($questions as $key=>$question) {
498  
-            $expout = $this->writequestion( $question , $shuffleanswers) . "\n";
499  
-            $expout = $this->presave_process( $expout );
500  
-            $key = $this->get_assesment_item_id($question);
501  
-            $exportquestions[$key] = $question;
502  
-            $exportquestions[$key]->exporttext = $expout;
503  
-        }
504  
-        return $exportquestions;
505  
-    }
506  
-
507  
-    /**
508  
-     * Creates the export text for a question
509  
-     *
510  
-     * @todo handle in-line media (specified in the question/subquestion/answer text) for course-level exports
511  
-     * @param object $question
512  
-     * @param bool $shuffleanswers whether or not to shuffle the answers
513  
-     * @param bool $courselevel whether or not this is a course-level export
514  
-     * @param string $path provide the path to copy question media files to, if $courselevel == true
515  
-     * @return string containing export text
516  
-     */
517  
-    function writequestion($question, $shuffleanswers = null, $courselevel = false, $path = '') {
518  
-        // turns question into string
519  
-        // question reflects database fields for general question and specific to type
520  
-        global $CFG;
521  
-        $expout = '';
522  
-        //need to unencode the html entities in the questiontext field.
523  
-        // the whole question object was earlier run throught htmlspecialchars in xml_entitize().
524  
-        $question->questiontext = html_entity_decode($question->questiontext, ENT_COMPAT);
525  
-
526  
-        $hasimage = empty($question->image) ? 0 : 1;
527  
-        $hassize = empty($question->mediax) ? 0 : 1;
528  
-
529  
-        $allowedtags = '<a><br><b><h1><h2><h3><h4><i><img><li><ol><strong><table><tr><td><th><u><ul><object>';  // all other tags will be stripped from question text
530  
-        $smarty =& $this->init_smarty();
531  
-        $assesmentitemid = $this->get_assesment_item_id($question);
532  
-        $question_type = $this->get_qtype( $question->qtype );
533  
-        $questionid = "question{$question->id}$question_type";
534  
-        $smarty->assign('question_has_image', $hasimage);
535  
-        $smarty->assign('hassize', $hassize);
536  
-        $smarty->assign('questionid', $questionid);
537  
-        $smarty->assign('assessmentitemidentifier', $assesmentitemid);
538  
-        $smarty->assign('assessmentitemtitle', $question->name);
539  
-        $smarty->assign('courselevelexport', $courselevel);
540  
-
541  
-        if ($question->qtype == MULTIANSWER) {
542  
-            $question->questiontext = strip_tags($question->questiontext, $allowedtags . '<intro>');
543  
-            $smarty->assign('questionText',  $this->get_cloze_intro($question->questiontext));
544  
-        } else {
545  
-            $smarty->assign('questionText',  strip_tags($question->questiontext, $allowedtags));
546  
-        }
547  
-
548  
-        $smarty->assign('question', $question);
549  
-        // the following two are left for compatibility; the templates should be changed, though, to make object tags for the questions
550  
-        //$smarty->assign('questionimage', $question->image);
551  
-        //$smarty->assign('questionimagealt', "image: $question->image");
552  
-
553  
-        // output depends on question type
554  
-        switch($question->qtype) {
555  
-        case TRUEFALSE:
556  
-            $qanswers = $question->options->answers;
557  
-            $answers[0] = (array)$qanswers[$question->options->trueanswer];
558  
-            $answers[0]['answer'] = get_string('true', 'qtype_truefalse');
559  
-            $answers[1] = (array)$qanswers[$question->options->falseanswer];
560  
-            $answers[1]['answer'] = get_string('false', 'qtype_truefalse');
561  
-
562  
-            if (!empty($shuffleanswers)) {
563  
-                $answers = $this->shuffle_things($answers);
564  
-            }
565  
-
566  
-            if (isset($question->response)) {
567  
-                $correctresponseid = $question->response[$questionid];
568  
-                if ($answers[0]['id'] == $correctresponseid) {
569  
-                    $correctresponse = $answers[0];
570  
-                } else {
571  
-                    $correctresponse = $answers[1];
572  
-                }
573  
-            } else {
574  
-                $correctresponse = '';
575  
-            }
576  
-
577  
-            $smarty->assign('correctresponse', $correctresponse);
578  
-            $smarty->assign('answers', $answers);
579  
-            $expout = $smarty->fetch('choice.tpl');
580  
-            break;
581  
-
582  
-        case MULTICHOICE:
583  
-            $answers = $this->objects_to_array($question->options->answers);
584  
-            $correctresponses = $this->get_correct_answers($answers);
585  
-            $correctcount = count($correctresponses);
586  
-            $smarty->assign('responsedeclarationcardinality', $question->options->single ? 'single' : 'multiple');
587  
-            $smarty->assign('operator', $question->options->single ? 'match' : 'member');
588  
-            $smarty->assign('correctresponses', $correctresponses);
589  
-            $smarty->assign('answers', $answers);
590  
-            $smarty->assign('maxChoices', $question->options->single ? '1' : count($answers));
591  
-            $smarty->assign('maxChoices', $question->options->single ? '1' : count($answers));
592  
-            $smarty->assign('shuffle', empty($shuffleanswers) ? 'false' : 'true');
593  
-            $smarty->assign('generalfeedback', $question->generalfeedback);
594  
-            $smarty->assign('correctfeedback', $question->options->correctfeedback);
595  
-            $smarty->assign('partiallycorrectfeedback', $question->options->partiallycorrectfeedback);
596  
-            $smarty->assign('incorrectfeedback', $question->options->incorrectfeedback);
597  
-            $expout = $smarty->fetch('choiceMultiple.tpl');
598  
-            break;
599  
-        case SHORTANSWER:
600  
-            $answers = $this->objects_to_array($question->options->answers);
601  
-            if (!empty($shuffleanswers)) {
602  
-                $answers = $this->shuffle_things($answers);
603  
-            }
604  
-
605  
-            $correctresponses = $this->get_correct_answers($answers);
606  
-            $correctcount = count($correctresponses);
607  
-
608  
-            $smarty->assign('responsedeclarationcardinality', $correctcount > 1 ? 'multiple' : 'single');
609  
-            $smarty->assign('correctresponses', $correctresponses);
610  
-            $smarty->assign('answers', $answers);
611  
-            $expout = $smarty->fetch('textEntry.tpl');
612  
-            break;
613  
-        case NUMERICAL:
614  
-            $qanswer = array_pop( $question->options->answers );
615  
-            $smarty->assign('lowerbound', $qanswer->answer - $qanswer->tolerance);
616  
-            $smarty->assign('upperbound', $qanswer->answer + $qanswer->tolerance);
617  
-            $smarty->assign('answer', $qanswer->answer);
618  
-            $expout = $smarty->fetch('numerical.tpl');
619  
-            break;
620  
-        case MATCH:
621  
-            $this->xml_entitize($question->options->subquestions);
622  
-            $subquestions = $this->objects_to_array($question->options->subquestions);
623  
-            if (!empty($shuffleanswers)) {
624  
-                $subquestions = $this->shuffle_things($subquestions);
625  
-            }
626  
-            $setcount = count($subquestions);
627  
-
628  
-            $smarty->assign('setcount', $setcount);
629  
-            $smarty->assign('matchsets', $subquestions);
630  
-            $expout = $smarty->fetch('match.tpl');
631  
-            break;
632  
-        case DESCRIPTION:
633  
-            $expout = $smarty->fetch('extendedText.tpl');
634  
-            break;
635  
-        // loss of get_answers() from quiz_embedded_close_qtype class during
636  
-        // Gustav's refactor breaks MULTIANSWER badly - one for another day!!
637  
-        /*
638  
-        case MULTIANSWER:
639  
-            $answers = $this->get_cloze_answers_array($question);
640  
-            $questions = $this->get_cloze_questions($question, $answers, $allowedtags);
641  
-
642  
-            $smarty->assign('cloze_trailing_text_id', CLOZE_TRAILING_TEXT_ID);
643  
-            $smarty->assign('answers', $answers);
644  
-            $smarty->assign('questions', $questions);
645  
-            $expout = $smarty->fetch('composite.tpl');
646  
-            break; */
647  
-        default:
648  
-            $smarty->assign('questionText', "This question type (Unknown: type $question_type)  has not yet been implemented");
649  
-            $expout = $smarty->fetch('notimplemented.tpl');
650  
-        }
651  
-
652  
-        // run through xml tidy function
653  
-        //$tidy_expout = $this->indent_xhtml( $expout, '    ' ) . "\n\n";
654  
-        //return $tidy_expout;
655  
-        return $expout;
656  
-    }
657  
-
658  
-/**
659  
- * Gets an id to use for a qti assesment item
660  
- *
661  
- * @param object $question
662  
- * @return string containing a qti assesment item id
663  
- */
664  
-    function get_assesment_item_id($question) {
665  
-        return "question{$question->id}";
666  
-    }
667  
-
668  
-/**
669  
- * gets the answers whose grade fraction > 0
670  
- *
671  
- * @param array $answers
672  
- * @return array (0-indexed) containing the answers whose grade fraction > 0
673  
- */
674  
-    function get_correct_answers($answers)
675  
-    {
676  
-        $correctanswers = array();
677  
-        foreach ($answers as $answer) {
678  
-            if ($answer['fraction'] > 0) {
679  
-                $correctanswers[] = $answer;
680  
-            }
681  
-        }
682  
-        return $correctanswers;
683  
-    }
684  
-
685  
-/**
686  
- * gets a new Smarty object, with the template and compile directories set
687  
- *
688  
- * @return object a smarty object
689  
- */
690  
-    function & init_smarty() {
691  
-        global $CFG;
692  
-
693  
-        // create smarty compile dir in dataroot
694  
-        $path = $CFG->dataroot."/smarty_c";
695  
-        if (!is_dir($path)) {
696  
-            if (!mkdir($path, $CFG->directorypermissions)) {
697  
-              print_error('cannotcreatepath', 'question', '', $path);
698  
-            }
699  
-        }
700  
-        $smarty = new Smarty;
701  
-        $smarty->template_dir = "{$CFG->dirroot}/question/format/qti_two/templates";
702  
-        $smarty->compile_dir  = "$path";
703  
-        return $smarty;
704  
-    }
705  
-
706  
-/**
707  
- * converts an array of objects to an array of arrays (not recursively)
708  
- *
709  
- * @param array $objectarray
710  
- * @return array - an array of answer arrays
711  
- */
712  
-    function objects_to_array($objectarray)
713  
-    {
714  
-        $arrayarray = array();
715  
-        foreach ($objectarray as $object) {
716  
-            $arrayarray[] = (array)$object;
717  
-        }
718  
-        return $arrayarray;
719  
-    }
720  
-
721  
-/**
722  
- * gets a question's cloze answer objects as arrays containing only arrays and basic data types
723  
- *
724  
- * @param object $question
725  
- * @return array - an array of answer arrays
726  
- */
727  
-    function get_cloze_answers_array($question) {
728  
-        $answers = $this->get_answers($question);
729  
-        $this->xml_entitize($answers);
730  
-        foreach ($answers as $answerkey => $answer) {
731  
-            $answers[$answerkey]->subanswers = $this->objects_to_array($answer->subanswers);
732  
-        }
733  
-        return $this->objects_to_array($answers);
734  
-    }
735  
-
736  
-/**
737  
- * gets an array with text and question arrays for the given cloze question
738  
- *
739  
- * To make smarty processing easier, the returned text and question sub-arrays have an equal number of elements.
740  
- * If it is necessary to add a dummy element to the question sub-array, the question will be given an id of CLOZE_TRAILING_TEXT_ID.
741  
- *
742  
- * @param object $question
743  
- * @param array $answers - an array of arrays containing the question's answers
744  
- * @param string $allowabletags - tags not to strip out of the question text (e.g. '<i><br>')
745  
- * @return array with text and question arrays for the given cloze question
746  
- */
747  
-     function get_cloze_questions($question, $answers, $allowabletags) {
748  
-        $questiontext = strip_tags($question->questiontext, $allowabletags);
749  
-        if (preg_match_all('/(.*){#([0-9]+)}/U', $questiontext, $matches)) {
750  
-            // matches[1] contains the text inbetween the question blanks
751  
-            // matches[2] contains the id of the question blanks (db: question_multianswer.positionkey)
752  
-
753  
-            // find any trailing text after the last {#XX} and add it to the array
754  
-            if (preg_match('/.*{#[0-9]+}(.*)$/', $questiontext, $tail)) {
755  
-                $matches[1][] = $tail[1];
756  
-                $tailadded = true;
757  
-            }
758  
-            $questions['text'] = $matches[1];
759  
-            $questions['question'] = array();
760  
-            foreach ($matches[2] as $key => $questionid) {
761  
-                foreach ($answers as $answer) {
762  
-                    if ($answer['positionkey'] == $questionid) {
763  
-                        $questions['question'][$key] = $answer;
764  
-                        break;
765  
-                    }
766  
-                }
767  
-            }
768  
-            if ($tailadded) {
769  
-                // to have a matching number of question and text array entries:
770  
-                $questions['question'][] = array('id'=>CLOZE_TRAILING_TEXT_ID, 'answertype'=>SHORTANSWER);
771  
-            }
772  
-
773  
-        } else {
774  
-            $questions['text'][0] = $question->questiontext;
775  
-            $questions['question'][0] = array('id'=>CLOZE_TRAILING_TEXT_ID, 'answertype'=>SHORTANSWER);
776  
-        }
777  
-
778  
-        return $questions;
779  
-    }
780  
-
781  
-/**
782  
- * strips out the <intro>...</intro> section, if any, and returns the text
783  
- *
784  
- * changes the text object passed to it.
785  
- *
786  
- * @param string $&text
787  
- * @return string the intro text, if there was an intro tag. '' otherwise.
788  
- */
789  
-    function get_cloze_intro(&$text) {
790  
-        if (preg_match('/(.*)?\<intro>(.+)?\<\/intro>(.*)/s', $text, $matches)) {
791  
-            $text = $matches[1] . $matches[3];
792  
-            return $matches[2];
793  
-        }
794  
-        else {
795  
-            return '';
796  
-        }
797  
-    }
798  
-
799  
-
800  
-/**
801  
- * adds qti metadata properties to the questions
802  
- *
803  
- * The passed array of questions is altered by this function
804  
- *
805  
- * @param &questions an array of question objects
806  
- */
807  
-    function add_qti_info(&$questions)
808  
-    {
809  
-        foreach ($questions as $key=>$question) {
810  
-            $questions[$key]->qtiinteractiontype = $this->get_qti_interaction_type($question->qtype);
811  
-            $questions[$key]->qtiscoreable = $this->get_qti_scoreable($question);
812  
-            $questions[$key]->qtisolutionavailable = $this->get_qti_solution_available($question);
813  
-        }
814  
-
815  
-    }
816  
-
817  
-/**
818  
- * returns whether or not a given question is scoreable
819  
- *
820  
- * @param object $question
821  
- * @return bool
822  
- */
823  
-    function get_qti_scoreable($question) {
824  
-        switch ($question->qtype) {
825  
-            case DESCRIPTION:
826  
-                return 'false';
827  
-            default:
828  
-                return 'true';
829  
-        }
830  
-    }
831  
-
832  
-/**
833  
- * returns whether or not a solution is available for a given question
834  
- *
835  
- * The results are based on whether or not Moodle stores answers for the given question type
836  
- *
837  
- * @param object $question
838  
- * @return bool
839  
- */
840  
-    function get_qti_solution_available($question) {
841  
-        switch($question->qtype) {
842  
-            case TRUEFALSE:
843  
-                return 'true';
844  
-            case MULTICHOICE:
845  
-                return 'true';
846  
-            case SHORTANSWER:
847  
-                return 'true';
848  
-            case NUMERICAL:
849  
-                return 'true';
850  
-            case MATCH:
851  
-                return 'true';
852  
-            case DESCRIPTION:
853  
-                return 'false';
854  
-            case MULTIANSWER:
855  
-                return 'true';
856  
-            default:
857  
-                return 'true';
858  
-        }
859  
-
860  
-    }
861  
-
862  
-/**
863  
- * maps a moodle question type to a qti 2.0 question type
864  
- *
865  
- * @param int type_id - the moodle question type
866  
- * @return string qti 2.0 question type
867  
- */
868  
-    function get_qti_interaction_type($type_id) {
869  
-        switch( $type_id ) {
870  
-        case TRUEFALSE:
871  
-            $name = 'choiceInteraction';
872  
-            break;
873  
-        case MULTICHOICE:
874  
-            $name = 'choiceInteraction';
875  
-            break;
876  
-        case SHORTANSWER:
877  
-            $name = 'textInteraction';
878  
-            break;
879  
-        case NUMERICAL:
880  
-            $name = 'textInteraction';
881  
-            break;
882  
-        case MATCH:
883  
-            $name = 'matchInteraction';
884  
-            break;
885  
-        case DESCRIPTION:
886  
-            $name = 'extendedTextInteraction';
887  
-            break;
888  
-        case MULTIANSWER:
889  
-            $name = 'textInteraction';
890  
-            break;
891  
-        default:
892  
-            $name = 'textInteraction';
893  
-        }
894  
-        return $name;
895  
-    }
896  
-
897  
-/**
898  
- * returns the given array, shuffled
899  
- *
900  
- *
901  
- * @param array $things
902  
- * @return array
903  
- */
904  
-    function shuffle_things($things) {
905  
-        $things = swapshuffle_assoc($things);
906  
-        $oldthings = $things;
907  
-        $things = array();
908  
-        foreach ($oldthings as $key=>$value) {
909  
-            $things[] = $value;      // This loses the index key, but doesn't matter
910  
-        }
911  
-        return $things;
912  
-    }
913  
-
914  
-/**
915  
- * returns a flattened image name - with all /, \ and : replaced with other characters
916  
- *
917  
- * used to convert a file or url to a qti-permissable identifier
918  
- *
919  
- * @param string name
920  
- * @return string
921  
- */
922  
-    function flatten_image_name($name) {
923  
-        return str_replace(array('/', '\\', ':'), array ('_','-','.'), $name);
924  
-    }
925