AC_4897, AC_4898, AC_4899: Multifile uploader fixes.
[acontent.git] / tests / import_test.php
1 <?php
2 /************************************************************************/
3 /* AContent                                                             */
4 /************************************************************************/
5 /* Copyright (c) 2010                                                   */
6 /* Inclusive Design Institute                                           */
7 /*                                                                      */
8 /* This program is free software. You can redistribute it and/or        */
9 /* modify it under the terms of the GNU General Public License          */
10 /* as published by the Free Software Foundation.                        */
11 /************************************************************************/
12
13 define('TR_INCLUDE_PATH', '../include/');
14 require_once(TR_INCLUDE_PATH.'vitals.inc.php');
15 require_once(TR_INCLUDE_PATH.'lib/pclzip.lib.php');
16 require_once(TR_INCLUDE_PATH.'lib/pclzip_callback.lib.php');
17 require_once(TR_INCLUDE_PATH.'lib/qti.inc.php'); 
18 require_once(TR_INCLUDE_PATH.'classes/QTI/QTIImport.class.php');
19 require_once(TR_INCLUDE_PATH.'classes/FileUtility.class.php');
20 require_once(TR_INCLUDE_PATH.'classes/DAO/CoursesDAO.class.php');
21 require_once(TR_INCLUDE_PATH.'classes/DAO/TestsQuestionsAssocDAO.class.php');
22
23 global $_course_id;
24
25 /* to avoid timing out on large files */
26 @set_time_limit(0);
27 $_SESSION['done'] = 1;
28
29 $element_path = array();
30 $character_data = '';
31 $test_title = '';
32 $resource_num = 0;
33 $qids = array();        //store all the question ids that's being inserted into the db by this import
34 $overwrite = false;     //files will not be overwrite and prompt
35
36 /* handle get */
37 if (isset($_POST['submit_yes'])){
38         $overwrite = true;
39 } elseif (isset($_POST['submit_no'])){
40         $msg->addFeedback('IMPORT_CANCELLED');
41         header('Location: index.php?_course_id='.$_course_id);
42         exit;
43 }
44
45 /* functions */
46 /* called at the start of en element */
47 /* builds the $path array which is the path from the root to the current element */
48 function startElement($parser, $name, $attrs) {
49         global $attributes, $element_path, $resource_num;
50         //save attributes.
51         switch($name) {
52                 case 'resource':
53                         $attributes[$name.$resource_num]['identifier'] = $attrs['identifier'];
54                         $attributes[$name.$resource_num]['href'] = $attrs['href'];
55                         $attributes[$name.$resource_num]['type'] = $attrs['type'];
56                         $resource_num++;
57                         break;
58                 case 'file':
59                         if(in_array('resource', $element_path)){
60                                 $attributes['resource'.($resource_num-1)]['file'][] = $attrs['href'];
61                         }
62                         break;
63                 case 'dependency':
64                         if(in_array('resource', $element_path)){
65                                 $attributes['resource'.($resource_num-1)]['dependency'][] = $attrs['identifierref'];
66                         }
67                         break;
68
69         }
70         array_push($element_path, $name);               
71 }
72
73 /* called when an element ends */
74 /* removed the current element from the $path */
75 function endElement($parser, $name) {
76         global $element_path, $test_title, $character_data;
77         switch($name) {
78                 case 'title':
79                         if (in_array('organization', $element_path)){
80                                 $test_title = $character_data;
81                         }
82         }
83         $character_data = '';
84         array_pop($element_path);
85 }
86
87 /* called when there is character data within elements */
88 /* constructs the $items array using the last entry in $path as the parent element */
89 function characterData($parser, $data){
90         global $character_data;
91         if (trim($data)!=''){
92                 $character_data .= preg_replace('/[\t\0\x0B]*/', '', $data);
93         }
94 }
95
96 //If overwrite hasn't been set to true, then the file has not been exported and still in the cache.
97 //otherwise, the zip file is extracted but has not been deleted (due to the confirmation).
98 if (!$overwrite){
99         if (!isset($_POST['submit_import'])) {
100                 /* just a catch all */
101                 
102                 $errors = array('FILE_MAX_SIZE', ini_get('post_max_size'));
103                 $msg->addError($errors);
104
105                 header('Location: index.php?_course_id='.$_course_id);
106                 exit;
107         } 
108
109
110         //Handles import
111         /*
112         if (isset($_POST['url']) && ($_POST['url'] != 'http://') ) {
113                 if ($content = @file_get_contents($_POST['url'])) {
114
115                         // save file to /content/
116                         $filename = substr(time(), -6). '.zip';
117                         $full_filename = TR_CONTENT_DIR . $filename;
118
119                         if (!$fp = fopen($full_filename, 'w+b')) {
120                                 echo "Cannot open file ($filename)";
121                                 exit;
122                         }
123
124                         if (fwrite($fp, $content, strlen($content) ) === FALSE) {
125                                 echo "Cannot write to file ($filename)";
126                                 exit;
127                         }
128                         fclose($fp);
129                 }       
130                 $_FILES['file']['name']     = $filename;
131                 $_FILES['file']['tmp_name'] = $full_filename;
132                 $_FILES['file']['size']     = strlen($content);
133                 unset($content);
134                 $url_parts = pathinfo($_POST['url']);
135                 $package_base_name_url = $url_parts['basename'];
136         }
137         */
138         $ext = pathinfo($_FILES['file']['name']);
139         $ext = $ext['extension'];
140
141         if ($ext != 'zip') {
142                 $msg->addError('IMPORTDIR_IMS_NOTVALID');
143         } else if ($_FILES['file']['error'] == 1) {
144                 $errors = array('FILE_MAX_SIZE', ini_get('upload_max_filesize'));
145                 $msg->addError($errors);
146         } else if ( !$_FILES['file']['name'] || (!is_uploaded_file($_FILES['file']['tmp_name']) && !$_POST['url'])) {
147                 $msg->addError('FILE_NOT_SELECTED');
148         } else if ($_FILES['file']['size'] == 0) {
149                 $msg->addError('IMPORTFILE_EMPTY');
150         } 
151 }
152
153 if ($msg->containsErrors()) {
154 //      if (isset($_GET['tile'])) {
155 //              header('Location: '.$_base_path.'tile/index.php');
156 //      } else {
157                 header('Location: index.php?_course_id='.$_course_id);
158 //      }
159         exit;
160 }
161
162 /* check if ../content/import/ exists */
163 $import_path = TR_CONTENT_DIR . 'import/';
164 $content_path = TR_CONTENT_DIR;
165
166 if (!is_dir($import_path)) {
167         if (!@mkdir($import_path, 0700)) {
168                 $msg->addError('IMPORTDIR_FAILED');
169         }
170 }
171
172 $import_path .= $_course_id.'/';
173 if (!$overwrite){
174         if (is_dir($import_path)) {
175                 FileUtility::clr_dir($import_path);
176         }
177
178         if (!@mkdir($import_path, 0700)) {
179                 $msg->addError('IMPORTDIR_FAILED');
180         }
181
182         /* extract the entire archive into TR_COURSE_CONTENT . import/$course using the call back function to filter out php files */
183         error_reporting(0);
184         $archive = new PclZip($_FILES['file']['tmp_name']);
185         if ($archive->extract(  PCLZIP_OPT_PATH,        $import_path,
186                                                         PCLZIP_CB_PRE_EXTRACT,  'preImportCallBack') == 0) {
187                 $msg->addError('IMPORT_FAILED');
188                 echo 'Error : '.$archive->errorInfo(true);
189                 FileUtility::clr_dir($import_path);
190                 header('Location: question_db.php?_course_id='.$_course_id);
191                 exit;
192         }
193         error_reporting(TR_ERROR_REPORTING);
194 }
195 /* get the course's max_quota */
196 $coursesDAO = new CoursesDAO();
197 $q_row  = $coursesDAO->get($_course_id);
198
199 if ($q_row['max_quota'] != TR_COURSESIZE_UNLIMITED) {
200         $zip_size_limit = $MaxCourseSize;
201         
202         $totalBytes   = FileUtility::dirsize($import_path);
203         
204         $total_after  = $zip_size_limit - $totalBytes;
205         
206         if (is_dir(TR_CONTENT_DIR . $_course_id.'/')) 
207         {
208                 $course_total = FileUtility::dirsize(TR_CONTENT_DIR . $_course_id.'/');
209                 $total_after  -= $course_total;
210         }
211         
212         if ($total_after < 0) {
213                 /* remove the content dir, since there's no space for it */
214                 $errors = array('NO_CONTENT_SPACE', number_format(-1*($total_after/TR_KBYTE_SIZE), 2 ) );
215                 $msg->addError($errors);
216                 
217                 // Clean up import path and inserted course row
218                 FileUtility::clr_dir($import_path);
219         
220                 header('Location: index.php?_course_id='.$_course_id);
221                 exit;
222         }
223 }
224
225 $ims_manifest_xml = @file_get_contents($import_path.'imsmanifest.xml');
226
227 if ($ims_manifest_xml === false) {
228         $msg->addError('NO_IMSMANIFEST');
229
230         if (file_exists($import_path . 'atutor_backup_version')) {
231                 $msg->addError('NO_IMS_BACKUP');
232         }
233
234         FileUtility::clr_dir($import_path);
235
236 //      if (isset($_GET['tile'])) {
237 //              header('Location: '.$_base_path.'tile/index.php');
238 //      } else {
239                 header('Location: index.php?_course_id='.$_course_id);
240 //      }
241         exit;
242 }
243
244 $xml_parser = xml_parser_create();
245
246 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */
247 xml_set_element_handler($xml_parser, 'startElement', 'endElement');
248 xml_set_character_data_handler($xml_parser, 'characterData');
249
250 if (!xml_parse($xml_parser, $ims_manifest_xml, true)) {
251         die(sprintf("XML error: %s at line %d",
252                                 xml_error_string(xml_get_error_code($xml_parser)),
253                                 xml_get_current_line_number($xml_parser)));
254 }
255
256 xml_parser_free($xml_parser);
257
258 //assign folder names
259 //if (!$package_base_name){
260 //      $package_base_name = substr($_FILES['file']['name'], 0, -4);
261 //}
262
263 //$package_base_name = strtolower($package_base_name);
264 //$package_base_name = str_replace(array('\'', '"', ' ', '|', '\\', '/', '<', '>', ':'), '_' , $package_base_name);
265 //$package_base_name = preg_replace("/[^A-Za-z0-9._\-]/", '', $package_base_name);
266
267 //if (is_dir(TR_CONTENT_DIR . $_SESSION['course_id'].'/'.$package_base_name)) {
268 //      echo 'Already exist: Quitting.  (Need better msg here)';
269 //      exit;
270 //      $package_base_name .= '_'.date('ymdHis');
271 //}
272
273 if ($package_base_path) {
274         $package_base_path = implode('/', $package_base_path);
275 }
276
277 //debug($attributes);
278 //Dependency handling
279 //$media_items = array();
280 $xml_items = array();
281 //foreach($attributes as $resource=>$attrs){
282 //      if ($attrs['type'] != 'webcontent'){
283 //              $media_items[$attrs['identifier']] = $attrs['file'];
284 //      }
285 //}
286
287 //Check if the files exist, if so, warn the user.
288 $existing_files = isQTIFileExist($attributes);
289 //debug($existing_files);
290 if (!$overwrite && !empty($existing_files)){
291         $existing_files = implode('<br/>', $existing_files);
292         require_once(TR_INCLUDE_PATH.'header.inc.php');
293 //      $msg->addConfirm(array('MEDIA_FILE_EXISTED', $existing_files));
294 //      $msg->printConfirm();
295         echo '<form action="" method="POST">';
296         echo '<div class="input-form">';
297         echo '<div class="row">';
298         $msg->printInfos(array('MEDIA_FILE_EXISTED', $existing_files));
299         echo '</div>';
300         echo '<div class="row buttons">';
301         echo '<input type="submit" class="" name="submit_yes" value="'._AT('yes').'"/>';
302         echo '<input type="submit" class="" name="submit_no" value="'._AT('no').'"/>';
303         echo '<input type="hidden" name="submit_import" value="submit_import" />';
304         ECHO '<input type="hidden" name="url" value="'.AT_print($_POST['url'], 'input.hidden').'" />';
305         echo '</div></div>';
306         echo '</form>';
307         require (TR_INCLUDE_PATH.'footer.inc.php');
308
309         exit;
310 }
311
312 //Get the XML file out and start importing them into our database.
313 //TODO: See question_import.php 287-289.
314 $qti_import = new QTIImport($import_path);
315 $qids = $qti_import->importQuestions($attributes);
316
317 //import test
318 $tid = $qti_import->importTest();
319
320 //associate question and tests
321 foreach ($qids as $order=>$qid){
322         if (isset($qti_import->weights[$order])){
323                 $weight = round($qti_import->weights[$order]);
324         } else {
325                 $weight = 0;
326         }
327         $new_order = $order + 1;
328 //      $sql = "INSERT INTO " . TABLE_PREFIX . "tests_questions_assoc" . 
329 //                      "(test_id, question_id, weight, ordering, required) " .
330 //                      "VALUES ($tid, $qid, $weight, $new_order, 0)";
331 //      $result = mysql_query($sql, $db);
332         $testsQuestionsAssocDAO = new TestsQuestionsAssocDAO();
333         $testsQuestionsAssocDAO->Create($tid, $qid, $weight, $new_order);
334 }
335 //debug('imported test');
336 if (!$msg->containsErrors()) {
337         $msg->addFeedback('IMPORT_SUCCEEDED');
338 }
339
340 //clear directory
341 FileUtility::clr_dir(TR_CONTENT_DIR . 'import/'.$_course_id);
342
343 header('Location: index.php?_course_id='.$_course_id);
344 exit;
345 ?>