AC_4897, AC_4898, AC_4899: Multifile uploader fixes.
[acontent.git] / docs / home / ims / ims_import.old.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
16 require_once(TR_INCLUDE_PATH.'classes/Utility.class.php'); /* for clr_dir() and preImportCallBack and dirsize() */
17 require_once(TR_INCLUDE_PATH.'classes/DAO/UsersDAO.class.php'); /* for clr_dir() and preImportCallBack and dirsize() */
18 require_once(TR_INCLUDE_PATH.'classes/DAO/CoursesDAO.class.php'); /* for clr_dir() and preImportCallBack and dirsize() */
19 require_once(TR_INCLUDE_PATH.'classes/DAO/ContentDAO.class.php'); /* for clr_dir() and preImportCallBack and dirsize() */
20 require_once(TR_INCLUDE_PATH.'../home/classes/ContentUtility.class.php'); /* for clr_dir() and preImportCallBack and dirsize() */
21 require_once(TR_INCLUDE_PATH.'lib/filemanager.inc.php'); /* for clr_dir() and preImportCallBack and dirsize() */
22 require_once(TR_INCLUDE_PATH.'lib/pclzip.lib.php');
23 require_once(TR_INCLUDE_PATH.'lib/qti.inc.php'); 
24 //require(TR_INCLUDE_PATH.'classes/QTI/QTIParser.class.php');   
25 require_once(TR_INCLUDE_PATH.'classes/QTI/QTIImport.class.php');
26 require_once(TR_INCLUDE_PATH.'classes/A4a/A4aImport.class.php');
27 //require(TR_INCLUDE_PATH.'../tools/ims/ns.inc.php');   //namespace, no longer needs, delete it after it's stable.
28 require_once(TR_INCLUDE_PATH.'classes/Weblinks/WeblinksParser.class.php');
29
30 global $_current_user;
31 /* make sure the user has author privilege */
32 if (!isset($_current_user) || !$_current_user->isAuthor())
33 {
34         $msg->addError('NO_PRIV');
35         include(TR_INCLUDE_PATH.'header.inc.php');
36         $msg->printAll(); 
37         include(TR_INCLUDE_PATH.'footer.inc.php');
38         exit;
39 }
40
41 /* to avoid timing out on large files */
42 @set_time_limit(0);
43 $_SESSION['done'] = 1;
44
45 $html_head_tags = array("style", "script");
46
47 $package_base_path = '';
48 $xml_base_path = '';
49 $element_path = array();
50 $imported_glossary = array();
51 $character_data = '';
52 $test_message = '';
53 $content_type = '';
54 $skip_ims_validation = false;
55
56
57 /**
58  * Validate all the XML in the package, including checking XSDs, missing data.
59  * @param       string          the path of the directory that contains all the package files
60  * @return      boolean         true if every file exists in the manifest, false if any is missing.
61  */
62 function checkResources($import_path){
63         global $items, $msg, $skip_ims_validation;
64
65         if (!is_dir($import_path)){
66                 return;
67         }
68
69         //if the package has access for all content, skip validation for now. 
70         //todo: import the XSD into our validator
71         if ($skip_ims_validation){
72                 return true;
73         }
74
75         //generate a file tree
76         $data = rscandir($import_path);
77
78         //check if every file is presented in the manifest
79         foreach($data as $filepath){
80                 $filepath = substr($filepath, strlen($import_path));
81
82                 //validate xml via its xsd/dtds
83                 if (preg_match('/(.*)\.xml/', $filepath)){
84                         $dom = new DOMDocument();
85                         $dom->load(realpath($import_path.$filepath));
86
87                         if (!@$dom->schemaValidate('main.xsd')){
88                                 $msg->addError('MANIFEST_FAILED_VALIDATION - '.$filepath);
89                         }
90                         //if this is the manifest file, we do not have to check for its existance.
91                         if (preg_match('/(.*)imsmanifest\.xml/', $filepath)){
92                                 continue;
93                         }
94                 }
95
96                 $flag = false;
97                 $file_exists_in_manifest = false;
98
99                 //check if every file in manifest indeed exists
100                 foreach($items as $name=>$fileinfo){
101                         if (is_array($fileinfo['file'])){
102                                 if(in_array($filepath, $fileinfo['file'])){
103                                         $file_exists_in_manifest = true;
104
105                                         //validate the xml by its schema
106                                         if (preg_match('/imsqti\_(.*)/', $fileinfo['type'])){
107                                                 $qti = new QTIParser($fileinfo['type']);
108                                                 $xml_content = @file_get_contents($import_path . $fileinfo['href']);
109                                                 $qti->parse($xml_content);
110                                                 if ($msg->containsErrors()){
111                                                         $flag = false;
112                                                 } else {
113                                                         $flag = true;
114                                                 }
115                                         } else {
116                                                 $flag = true;
117                                         }
118                                 }
119                         }
120                 }
121
122                 //check if all the files exists in the manifest, if not, throw error.
123                 if (!$file_exists_in_manifest){
124                         $msg->addError('MANIFEST_NOT_WELLFORM: MISSING REFERENCES');
125                         break;
126                 }
127
128                 if ($flag == false){
129                         //add an error message if it doesn't have any. 
130                         if (!$msg->containsErrors()){
131                                 $msg->addError('MANIFEST_NOT_WELLFORM: MISSING REFERENCES');
132                         }
133                         return false;
134                 }
135         }
136         return true;
137 }
138
139 /*
140  * @example rscandir(dirname(__FILE__).'/'));
141  * @param string $base
142  * @param array $omit
143  * @param array $data
144  * @return array
145  */
146 function rscandir($base='', &$data=array()) {
147  
148   $array = array_diff(scandir($base), array('.', '..')); # remove ' and .. from the array */
149   
150   foreach($array as $value) : /* loop through the array at the level of the supplied $base */
151  
152     if (is_dir($base.$value)) : /* if this is a directory */
153 //        don't save the directory name
154 //        $data[] = $base.$value.'/'; /* add it to the $data array */
155       $data = rscandir($base.$value.'/', $data); /* then make a recursive call with the
156       current $value as the $base supplying the $data array to carry into the recursion */
157      
158     elseif (is_file($base.$value)) : /* else if the current $value is a file */
159       $data[] = $base.$value; /* just add the current $value to the $data array */
160      
161     endif;
162    
163   endforeach;
164  
165   return $data; // return the $data array
166  
167 }
168
169 /**
170  * Function to restructure the $items.  So that old import will merge the top page into its children, and
171  * create a new folder on top of it
172  */
173 function rehash($items){
174         global $order;
175         $parent_page_maps = array();    //old=>new
176         $temp_popped_items = array();
177         $rehashed_items = array();      //the reconstructed array
178         foreach($items as $id => $content){
179                 $parent_obj = $items[$content['parent_content_id']];
180                 $rehashed_items[$id] = $content;        //copy
181
182                 //first check if there exists a mapping for this item, if so, simply replace is and next.
183                 if (isset($parent_page_maps[$content['parent_content_id']])){
184                         $rehashed_items [$id]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']];
185                         $rehashed_items [$id]['ordering']++;
186                 } 
187                 //If its parent page is a top page and have an identiferref
188                 else if (isset($parent_obj) && isset($parent_obj['href'])){                     
189                         if (!isset($parent_obj['href'])){
190                                 //check if this top page is already a folder, if so, next.
191                                 continue;
192                         }
193                         //else, make its parent page to a folder
194                         $new_item['title'] = $parent_obj['title'];
195                         $new_item['parent_content_id'] = $parent_obj['parent_content_id'];
196                         $new_item['ordering'] = $parent_obj['ordering'];
197
198                 //assign this new parent folder to the pending items array
199                         $new_item_name = $content['parent_content_id'].'_FOLDER';
200                         //a not so brilliant way to append the folder in its appropriate position
201                         $reordered_hashed_items = array();  //use to store the new rehashed item with the correct item order
202                         foreach($rehashed_items as $rh_id=>$rh_content){
203                             if ($rh_id == $content['parent_content_id']){
204                                 //add the folder in before the parent subpage.
205                                 $reordered_hashed_items[$new_item_name] = $new_item;
206                             }
207                             $reordered_hashed_items[$rh_id] = $rh_content;  //clone
208                         }
209                         $rehashed_items = $reordered_hashed_items;  //replace it back
210                         unset($reordered_hashed_items);
211                         $parent_page_maps[$content['parent_content_id']] = $new_item_name;  //save this page on the hash map
212
213                         //reconstruct the parent
214                         $rehashed_items[$content['parent_content_id']]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']];
215                         $rehashed_items[$content['parent_content_id']]['ordering'] = 0; //always the first one.
216
217                         //reconstruct itself
218                         $rehashed_items[$id]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']];
219                         $rehashed_items[$id]['ordering']++;
220
221                 }
222         }
223
224         return $rehashed_items;
225 }
226
227
228 /** 
229  * This function will take the test accessment XML and add these to the database.
230  * @param       string  The path of the XML, without the import_path.
231  * @param       mixed   An item singleton.  Contains the info of this item, namely, the accessment details.
232  *                                      The item must be an object created by the ims class.
233  * @param       string  the import path
234  * @return      mixed   An Array that contains all the question IDs that have been imported.
235  */
236  function addQuestions($xml, $item, $import_path){
237         $qti_import = new QTIImport($import_path);
238
239         $tests_xml = $import_path.$xml;
240         
241         //Mimic the array for now.
242         $test_attributes['resource']['href'] = $item['href'];
243         $test_attributes['resource']['type'] = preg_match('/imsqti_xmlv1p2/', $item['type'])==1?'imsqti_xmlv1p2':'imsqti_xmlv1p1';
244         $test_attributes['resource']['file'] = $item['file'];
245
246         //Get the XML file out and start importing them into our database.
247         //TODO: See question_import.php 287-289.
248         $qids = $qti_import->importQuestions($test_attributes);
249
250         return $qids;
251  }
252
253
254
255         /* called at the start of en element */
256         /* builds the $path array which is the path from the root to the current element */
257         function startElement($parser, $name, $attrs) {
258                 global $items, $path, $package_base_path, $import_path;
259                 global $element_path;
260                 global $xml_base_path, $test_message, $content_type;
261                 global $current_identifier, $msg;
262
263                 if ($element_path === array('manifest', 'metadata', 'imsmd:lom', 'imsmd:general', 'imsmd:title') && $name == 'imsmd:langstring') {
264                         global $package_primay_lang;
265                         $package_primay_lang = trim($attrs['xml:lang']);
266                 }
267                 
268                 if ($name == 'manifest' && isset($attrs['xml:base']) && $attrs['xml:base']) {
269                         $xml_base_path = $attrs['xml:base'];
270                 } else if ($name == 'file') {
271                         // check if it misses file references
272                         if(!isset($attrs['href']) || $attrs['href']==''){
273                                 $msg->addError('MANIFEST_NOT_WELLFORM');
274                         }
275
276                         // special case for webCT content packages that don't specify the `href` attribute 
277                         // with the `<resource>` element.
278                         // we take the `href` from the first `<file>` element.
279                         if (isset($items[$current_identifier]) && ($items[$current_identifier]['href'] == '')) {
280                                 $attrs['href'] = urldecode($attrs['href']);
281                                 $items[$current_identifier]['href'] = $attrs['href'];
282                         }
283
284                         $temp_path = pathinfo($attrs['href']);
285                         $temp_path = explode('/', $temp_path['dirname']);
286
287                         //for IMSCC, assume that all resources lies in the same folder, except styles.css
288                         if ($items[$current_identifier]['type']=='webcontent'){
289                                 if ($package_base_path=="") {
290                                         $package_base_path = $temp_path;
291                                 } 
292                                 elseif (is_array($package_base_path) && $content_type != 'IMS Common Cartridge') {
293                                         //if this is a content package, we want only intersection
294                                         $package_base_path = array_intersect($package_base_path, $temp_path);
295                                         $temp_path = $package_base_path;
296                                 }
297                                 //added these 2 lines in so that pictures would load.  making the elseif above redundant.
298                                 //if there is a bug for pictures not load, then it's the next 2 lines.
299                                 $package_base_path = array_intersect($package_base_path, $temp_path);
300                                 $temp_path = $package_base_path;
301                         }
302                         $items[$current_identifier]['new_path'] = implode('/', $temp_path);     
303                         if (    isset($_POST['allow_test_import']) && isset($items[$current_identifier]) 
304                                                 && preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $attrs['href'])) {
305                                 $items[$current_identifier]['tests'][] = $attrs['href'];
306                         } 
307                         if (    isset($_POST['allow_a4a_import']) && isset($items[$current_identifier])) {
308                                 $items[$current_identifier]['a4a_import_enabled'] = true;
309                         }
310                 } else if (($name == 'item') && ($attrs['identifierref'] != '')) {
311                         $path[] = $attrs['identifierref'];
312                 } else if (($name == 'item') && ($attrs['identifier'])) {
313                         $path[] = $attrs['identifier'];
314 //              } else if (($name == 'resource') && is_array($items[$attrs['identifier']]))  {
315                 } else if (($name == 'resource')) {
316                         $current_identifier = $attrs['identifier'];
317                         $items[$current_identifier]['type'] = $attrs['type'];
318                         if ($attrs['href']) {
319                                 $attrs['href'] = urldecode($attrs['href']);
320
321                                 $items[$attrs['identifier']]['href'] = $attrs['href'];
322
323                                 // href points to a remote url
324                                 if (preg_match('/^http.*:\/\//', trim($attrs['href'])))
325                                         $items[$attrs['identifier']]['new_path'] = '';
326                                 else // href points to local file
327                                 {
328                                         $temp_path = pathinfo($attrs['href']);
329                                         $temp_path = explode('/', $temp_path['dirname']);
330                                         if (empty($package_base_path)) {
331                                                 $package_base_path = $temp_path;
332                                         } 
333                                         $items[$attrs['identifier']]['new_path'] = implode('/', $temp_path);
334                                 }
335                         }
336                 } else if ($name=='dependency' && $attrs['identifierref']!='') {
337                         //if there is a dependency, attach it to the item array['file']
338                         $items[$current_identifier]['dependency'][] = $attrs['identifierref'];
339                 }
340                 if (($name == 'item') && ($attrs['parameters'] != '')) {
341                         $items[$attrs['identifierref']]['test_message'] = $attrs['parameters'];
342                 }
343                 if ($name=='file'){
344                         if(!isset($items[$current_identifier]) && $attrs['href']!=''){
345                                 $items[$current_identifier]['href']      = $attrs['href'];
346                         }
347                         if (file_exists($import_path.$attrs['href'])){
348                                 $items[$current_identifier]['file'][] = $attrs['href'];
349                         } else {
350                                 $msg->addError('IMS_FILES_MISSING');
351                         }
352                 }               
353                 if ($name=='cc:authorizations'){
354                         //don't have authorization setup.
355                         $msg->addError('IMS_AUTHORIZATION_NOT_SUPPORT');
356                 }
357         array_push($element_path, $name);
358 }
359
360         /* called when an element ends */
361         /* removed the current element from the $path */
362         function endElement($parser, $name) {
363                 global $path, $element_path, $my_data, $items;
364                 global $current_identifier, $skip_ims_validation;
365                 global $msg, $content_type;             
366                 static $resource_num = 0;
367                 
368                 if ($name == 'item') {
369                         array_pop($path);
370                 } 
371
372                 //check if this is a test import
373                 if ($name == 'schema'){
374                         if (trim($my_data)=='IMS Question and Test Interoperability'){                  
375                                 $msg->addError('IMPORT_FAILED');
376                         } 
377                         $content_type = trim($my_data);
378                 }
379
380                 //Handles A4a
381                 if ($current_identifier != ''){
382                         $my_data = trim($my_data);
383                         $last_file_name = $items[$current_identifier]['file'][(sizeof($items[$current_identifier]['file']))-1];
384
385                         if ($name=='originalAccessMode'){                               
386                                 if (in_array('accessModeStatement', $element_path)){
387                                         $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['access_stmt_originalAccessMode'][] = $my_data;
388                                 } elseif (in_array('adaptationStatement', $element_path)){
389                                         $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['adapt_stmt_originalAccessMode'][] = $my_data;
390                                 }                       
391                         } elseif (($name=='language') && in_array('accessModeStatement', $element_path)){
392                                 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['language'][] = $my_data;
393                         } elseif ($name=='hasAdaptation') {
394                                 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['hasAdaptation'][] = $my_data;
395                         } elseif ($name=='isAdaptationOf'){
396                                 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['isAdaptationOf'][] = $my_data;
397                         } elseif ($name=='accessForAllResource'){
398                                 /* the head node of accessForAll Metadata, if this exists in the manifest. Skip XSD validation,
399                                  * because A4a doesn't have a xsd yet.  Our access for all is based on ISO which will not pass 
400                                  * the current IMS validation.  
401                                  * Also, since ATutor is the only one (as of Oct 21, 2009) that exports IMS with access for all
402                                  * content, we can almost assume that any ims access for all content is by us, and is valid. 
403                                  */
404                                 $skip_ims_validation = true;
405                                 $resource_num++;
406                         } elseif($name=='file'){
407                                 $resource_num = 0;      //reset resournce number to 0 when the file tags ends
408                         }
409                 }
410
411                 if ($element_path === array('manifest', 'metadata', 'imsmd:lom', 'imsmd:general', 'imsmd:title', 'imsmd:langstring')) {
412                         global $package_base_name;
413                         $package_base_name = trim($my_data);
414                 }
415
416                 if ($element_path === array('manifest', 'metadata', 'imsmd:lom', 'imsmd:general', 'imsmd:description', 'imsmd:langstring')) {
417                         global $package_base_description;
418                         $package_base_description = trim($my_data);
419                 }
420
421                 array_pop($element_path);
422                 $my_data = '';
423         }
424
425         /* called when there is character data within elements */
426         /* constructs the $items array using the last entry in $path as the parent element */
427         function characterData($parser, $data){
428                 global $path, $items, $order, $my_data, $element_path;
429                 global $current_identifier;
430
431                 $str_trimmed_data = trim($data);
432                                 
433                 if (!empty($str_trimmed_data)) {
434                         $size = count($path);
435                         if ($size > 0) {
436                                 $current_item_id = $path[$size-1];
437                                 if ($size > 1) {
438                                         $parent_item_id = $path[$size-2];
439                                 } else {
440                                         $parent_item_id = 0;
441                                 }
442
443                                 if (isset($items[$current_item_id]['parent_content_id']) && is_array($items[$current_item_id])) {
444
445                                         /* this item already exists, append the title           */
446                                         /* this fixes {\n, \t, `, &} characters in elements */
447
448                                         /* horible kludge to fix the <ns2:objectiveDesc xmlns:ns2="http://www.utoronto.ca/atrc/tile/xsd/tile_objective"> */
449                                         /* from TILE */
450                                         if (in_array('accessForAllResource', $element_path)){
451                                                 //skip this tag
452                                         } elseif ($element_path[count($element_path)-1] != 'ns1:objectiveDesc') {
453                                                 $items[$current_item_id]['title'] .= $data;
454                                         }
455         
456                                 } else {
457                                         $order[$parent_item_id]++;
458                                         $item_tmpl = array(     'title'                 => $data,
459                                                                                 'parent_content_id' => $parent_item_id,
460                                                                                 'ordering'                      => $order[$parent_item_id]-1);
461                                         //append other array values if it exists
462                                         if (is_array($items[$current_item_id])){
463                                                 $items[$current_item_id] = array_merge($items[$current_item_id], $item_tmpl);
464                                         } else {
465                                                 $items[$current_item_id] = $item_tmpl;
466                                         }
467                                 }
468                         }
469                 }
470
471                 $my_data .= $data;
472         }
473
474         /* glossary parser: */
475         function glossaryStartElement($parser, $name, $attrs) {
476                 global $element_path;
477
478                 array_push($element_path, $name);
479         }
480
481         /* called when an element ends */
482         /* removed the current element from the $path */
483         function glossaryEndElement($parser, $name) {
484                 global $element_path, $my_data, $imported_glossary;
485                 static $current_term;
486
487                 if ($element_path === array('glossary', 'item', 'term')) {
488                         $current_term = $my_data;
489
490                 } else if ($element_path === array('glossary', 'item', 'definition')) {
491                         $imported_glossary[trim($current_term)] = trim($my_data);
492                 }
493
494                 array_pop($element_path);
495                 $my_data = '';
496         }
497
498         function glossaryCharacterData($parser, $data){
499                 global $my_data;
500
501                 $my_data .= $data;
502         }
503
504 if (!isset($_POST['submit']) && !isset($_POST['cancel'])) {
505         /* just a catch all */
506         
507         $errors = array('FILE_MAX_SIZE', ini_get('post_max_size'));
508         $msg->addError($errors);
509
510         header('Location: ../index.php');
511         exit;
512 } else if (isset($_POST['cancel'])) {
513         $msg->addFeedback('IMPORT_CANCELLED');
514
515         header('Location: ../index.php');
516         exit;
517 }
518
519 if (isset($_POST['url']) && ($_POST['url'] != 'http://') ) {
520         if ($content = @file_get_contents($_POST['url'])) {
521
522                 // save file to /content/
523                 $filename = substr(time(), -6). '.zip';
524                 $full_filename = TR_TEMP_DIR . $filename;
525
526                 if (!$fp = fopen($full_filename, 'w+b')) {
527                         echo "Cannot open file ($filename)";
528                         exit;
529                 }
530
531                 if (fwrite($fp, $content, strlen($content) ) === FALSE) {
532                         echo "Cannot write to file ($filename)";
533                         exit;
534                 }
535                 fclose($fp);
536         }       
537         $_FILES['file']['name']     = $filename;
538         $_FILES['file']['tmp_name'] = $full_filename;
539         $_FILES['file']['size']     = strlen($content);
540         unset($content);
541         $url_parts = pathinfo($_POST['url']);
542         $package_base_name_url = $url_parts['basename'];
543 }
544 $ext = pathinfo($_FILES['file']['name']);
545 $ext = $ext['extension'];
546
547 if ($ext != 'zip') {
548         $msg->addError('IMPORTDIR_IMS_NOTVALID');
549 } else if ($_FILES['file']['error'] == 1) {
550         $errors = array('FILE_MAX_SIZE', ini_get('upload_max_filesize'));
551         $msg->addError($errors);
552 } else if ( !$_FILES['file']['name'] || (!is_uploaded_file($_FILES['file']['tmp_name']) && !$_POST['url'])) {
553         $msg->addError('FILE_NOT_SELECTED');
554 } else if ($_FILES['file']['size'] == 0) {
555         $msg->addError('IMPORTFILE_EMPTY');
556
557
558 if ($msg->containsErrors()) {
559         if (isset($_GET['tile'])) {
560                 header('Location: '.$_base_path.'tools/tile/index.php');
561         } else {
562                 header('Location: ../index.php');
563         }
564         exit;
565 }
566
567 /* check if ../content/import/ exists */
568 $import_path = TR_TEMP_DIR . 'import/';
569 $content_path = TR_TEMP_DIR;
570
571 if (!is_dir($import_path)) {
572         if (!@mkdir($import_path, 0700)) {
573                 $msg->addError('IMPORTDIR_FAILED');
574         }
575 }
576
577 $import_path .= Utility::getRandomStr(16).'/';
578 if (is_dir($import_path)) {
579         clr_dir($import_path);
580 }
581
582 if (!@mkdir($import_path, 0700)) {
583         $msg->addError('IMPORTDIR_FAILED');
584 }
585
586 if ($msg->containsErrors()) {
587         if (isset($_GET['tile'])) {
588                 header('Location: '.$_base_path.'tools/tile/index.php');
589         } else {
590                 header('Location: ../index.php');
591         }
592         exit;
593 }
594
595 /* extract the entire archive into TR_COURSE_CONTENT . import/$course using the call back function to filter out php files */
596 error_reporting(0);
597 $archive = new PclZip($_FILES['file']['tmp_name']);
598 if ($archive->extract(  PCLZIP_OPT_PATH,        $import_path,
599                                                 PCLZIP_CB_PRE_EXTRACT,  'preImportCallBack') == 0) {
600         $msg->addError('IMPORT_FAILED');
601         echo 'Error : '.$archive->errorInfo(true);
602         clr_dir($import_path);
603         header('Location: ../index.php');
604         exit;
605 }
606 error_reporting(TR_ERROR_REPORTING);
607
608 /* get the course's max_quota */
609 //$sql  = "SELECT max_quota FROM ".TABLE_PREFIX."courses WHERE course_id=$course_id";
610 //$result = mysql_query($sql, $db);
611 //$q_row        = mysql_fetch_assoc($result);
612 //
613 //if ($q_row['max_quota'] != TR_COURSESIZE_UNLIMITED) {
614 //
615 //      if ($q_row['max_quota'] == TR_COURSESIZE_DEFAULT) {
616 //              $q_row['max_quota'] = $MaxCourseSize;
617 //      }
618 //      $totalBytes   = dirsize($import_path);
619 //      $course_total = dirsize($import_path);
620 //      $total_after  = $q_row['max_quota'] - $course_total - $totalBytes + $MaxCourseFloat;
621 //
622 //      if ($total_after < 0) {
623 //              /* remove the content dir, since there's no space for it */
624 //              $errors = array('NO_CONTENT_SPACE', number_format(-1*($total_after/TR_KBYTE_SIZE), 2 ) );
625 //              $msg->addError($errors);
626 //              
627 //              clr_dir($import_path);
628 //
629 //              if (isset($_GET['tile'])) {
630 //                      header('Location: '.$_base_path.'tools/tile/index.php');
631 //              } else {
632 //                      header('Location: index.php');
633 //              }
634 //              exit;
635 //      }
636 //}
637
638
639 $items = array(); /* all the content pages */
640 $order = array(); /* keeps track of the ordering for each content page */
641 $path  = array();  /* the hierarchy path taken in the menu to get to the current item in the manifest */
642 $dependency_files = array(); /* the file path for the dependency files */
643
644 /*
645 $items[content_id/resource_id] = array(
646                                                                         'title'
647                                                                         'real_content_id' // calculated after being inserted
648                                                                         'parent_content_id'
649                                                                         'href'
650                                                                         'ordering'
651                                                                         );
652 */
653
654 $ims_manifest_xml = @file_get_contents($import_path.'imsmanifest.xml');
655
656 if ($ims_manifest_xml === false) {
657         $msg->addError('NO_IMSMANIFEST');
658
659         if (file_exists($import_path . 'atutor_backup_version')) {
660                 $msg->addError('NO_IMS_BACKUP');
661         }
662
663         clr_dir($import_path);
664
665         if (isset($_GET['tile'])) {
666                 header('Location: '.$_base_path.'tools/tile/index.php');
667         } else {
668                 header('Location: ../index.php');
669         }
670         exit;
671 }
672
673 $xml_parser = xml_parser_create();
674
675 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */
676 xml_set_element_handler($xml_parser, 'startElement', 'endElement');
677 xml_set_character_data_handler($xml_parser, 'characterData');
678
679 if (!xml_parse($xml_parser, $ims_manifest_xml, true)) {
680         die(sprintf("XML error: %s at line %d",
681                                 xml_error_string(xml_get_error_code($xml_parser)),
682                                 xml_get_current_line_number($xml_parser)));
683 }
684
685 xml_parser_free($xml_parser);
686 // skip glossary
687 /* check if the glossary terms exist */
688 //$glossary_path = '';
689 //if ($content_type == 'IMS Common Cartridge'){
690 //      $glossary_path = 'GlossaryItem/';
691 //}
692 //if (file_exists($import_path . $glossary_path . 'glossary.xml')){
693 //      $glossary_xml = @file_get_contents($import_path.$glossary_path.'glossary.xml');
694 //      $element_path = array();
695 //
696 //      $xml_parser = xml_parser_create();
697 //
698 //      /* insert the glossary terms into the database (if they're not in there already) */
699 //      /* parse the glossary.xml file and insert the terms */
700 //      xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */
701 //      xml_set_element_handler($xml_parser, 'glossaryStartElement', 'glossaryEndElement');
702 //      xml_set_character_data_handler($xml_parser, 'glossaryCharacterData');
703 //
704 //      if (!xml_parse($xml_parser, $glossary_xml, true)) {
705 //              die(sprintf("XML error: %s at line %d",
706 //                                      xml_error_string(xml_get_error_code($xml_parser)),
707 //                                      xml_get_current_line_number($xml_parser)));
708 //      }
709 //      xml_parser_free($xml_parser);
710 //      $contains_glossary_terms = true;
711 //      foreach ($imported_glossary as $term => $defn) {
712 //              if (!$glossary[urlencode($term)]) {
713 //                      $sql = "INSERT INTO ".TABLE_PREFIX."glossary VALUES (NULL, $_SESSION[course_id], '$term', '$defn', 0)";
714 //                      mysql_query($sql, $db); 
715 //              }
716 //      }
717 //}
718
719 // Check if all the files exists in the manifest, iff it's a IMS CC package.
720 if ($content_type == 'IMS Common Cartridge') {
721         checkResources($import_path);
722 }
723
724 // Check if there are any errors during parsing.
725 if ($msg->containsErrors()) {
726         if (isset($_GET['tile'])) {
727                 header('Location: '.$_base_path.'tools/tile/index.php');
728         } else {
729                 header('Location: ../index.php');
730         }
731         exit;
732 }
733
734 /* initialize DAO objects */
735 $coursesDAO = new CoursesDAO();
736 $contentDAO = new ContentDAO();
737
738 /* generate a unique new package base path based on the package file name and date as needed. */
739 /* the package name will be the dir where the content for this package will be put, as a result */
740 /* the 'content_path' field in the content table will be set to this path. */
741 /* $package_base_name_url comes from the URL file name (NOT the file name of the actual file we open)*/
742 if (!$package_base_name && $package_base_name_url) {
743         $package_base_name = substr($package_base_name_url, 0, -4);
744 } else if (!$package_base_name) {
745         $package_base_name = substr($_FILES['file']['name'], 0, -4);
746 }
747
748 // create course
749 if (isset($_POST['hide_course']))
750         $access = 'private';
751 else
752         $access = 'public';
753
754 $course_id = $coursesDAO->Create($_SESSION['user_id'], 'top', $access, $package_base_name, $package_base_description, 
755              '', '', '', '', $package_primay_lang, '', '');
756
757 $package_base_name = strtolower($package_base_name);
758 $package_base_name = str_replace(array('\'', '"', ' ', '|', '\\', '/', '<', '>', ':'), '_' , $package_base_name);
759 $package_base_name = preg_replace("/[^A-Za-z0-9._\-]/", '', $package_base_name);
760
761 if (is_dir(TR_TEMP_DIR . $course_id.'/'.$package_base_name)) {
762         $package_base_name .= '_'.date('ymdHis');
763 }
764
765 if ($package_base_path) {
766         $package_base_path = implode('/', $package_base_path);
767 } elseif (empty($package_base_path)){
768         $package_base_path = '';
769 }
770
771 if ($xml_base_path) {
772         $package_base_path = $xml_base_path . $package_base_path;
773
774         mkdir(TR_TEMP_DIR .$course_id.'/'.$xml_base_path);
775         $package_base_name = $xml_base_path . $package_base_name;
776 }
777
778 /* get the top level content ordering offset */
779 //$sql  = "SELECT MAX(ordering) AS ordering FROM ".TABLE_PREFIX."content WHERE course_id=$_SESSION[course_id] AND content_parent_id=$cid";
780 //$result = mysql_query($sql, $db);
781 //$row  = mysql_fetch_assoc($result);
782 $order_offset = $contentDAO->getMaxOrdering($course_id, 0); /* it's nice to have a real number to deal with */
783 $lti_offset = array();  //since we don't need lti tools, the ordering needs to be subtracted
784 //reorder the items stack
785 $items = rehash($items);
786
787 foreach ($items as $item_id => $content_info) 
788 {       
789         //formatting field, default 1
790         $content_formatting = 1;        //CONTENT_TYPE_CONTENT
791
792         //if this is any of the LTI tools, skip it. (ie. Discussion Tools, Weblinks, etc)
793         //take this condition out once the LTI tool kit is implemented.
794         if ($content_info['type']=='imsdt_xmlv1p0'){
795                 $lti_offset[$content_info['parent_content_id']]++;
796                 continue;
797         }
798
799         //don't want to display glossary as a page
800         if ($content_info['href']== $glossary_path . 'glossary.xml'){
801                 continue;
802         }
803
804         //handle the special case of cc import, where there is no content association. The resource should
805         //still be imported.
806         if(!isset($content_info['parent_content_id'])){
807                 //if this is a question bank 
808                 if ($content_info['type']=="imsqti_xmlv1p2/imscc_xmlv1p0/question-bank"){
809                         addQuestions($content_info['href'], $content_info, $import_path);
810                 }
811         }
812
813         //if it has no title, most likely it is not a page but just a normal item, skip it
814         if (!isset($content_info['title'])){
815                 continue;
816         }
817         
818         //check dependency immediately, then handles it
819         $head = '';
820         if (is_array($content_info['dependency']) && !empty($content_info['dependency'])){
821                 foreach($content_info['dependency'] as $dependency_ref){
822                         //handle styles 
823                         if (preg_match('/(.*)\.css$/', $items[$dependency_ref]['href'])){
824                                 //calculate where this is based on our current base_href. 
825                                 //assuming the dependency folders are siblings of the item
826                                 $head = '<link rel="stylesheet" type="text/css" href="../'.$items[$dependency_ref]['href'].'" />';
827                         }
828                 }
829         }
830         
831         // remote href
832         if (preg_match('/^http.*:\/\//', trim($content_info['href'])) )
833         {
834                 $content = '<a href="'.$content_info['href'].'" target="_blank">'.$content_info['title'].'</a>';
835         }
836         else
837         {
838                 if (isset($content_info['href'], $xml_base_path)) {
839                         $content_info['href'] = $xml_base_path . $content_info['href'];
840                 }
841                 if (!isset($content_info['href'])) {
842                         // this item doesn't have an identifierref. so create an empty page.
843                         // what we called a folder according to v1.2 Content Packaging spec
844                         // Hop over
845                         $content = '';
846                         $ext = '';
847                         $last_modified = date('Y-m-d H:i:s');
848                 } else {
849                         $file_info = @stat($import_path.$content_info['href']);
850                         if ($file_info === false) {
851                                 continue;
852                         }
853                 
854                         $path_parts = pathinfo($import_path.$content_info['href']);
855                         $ext = strtolower($path_parts['extension']);
856
857                         $last_modified = date('Y-m-d H:i:s', $file_info['mtime']);
858                 }
859                 if (in_array($ext, array('gif', 'jpg', 'bmp', 'png', 'jpeg'))) {
860                         /* this is an image */
861                         $content = '<img src="'.$content_info['href'].'" alt="'.$content_info['title'].'" />';
862                 } else if ($ext == 'swf') {
863                         /* this is flash */
864             /* Using default size of 550 x 400 */
865
866                         $content = '<object type="application/x-shockwave-flash" data="' . $content_info['href'] . '" width="550" height="400"><param name="movie" value="'. $content_info['href'] .'" /></object>';
867
868                 } else if ($ext == 'mov') {
869                         /* this is a quicktime movie  */
870             /* Using default size of 550 x 400 */
871
872                         $content = '<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" width="550" height="400" codebase="http://www.apple.com/qtactivex/qtplugin.cab"><param name="src" value="'. $content_info['href'] . '" /><param name="autoplay" value="true" /><param name="controller" value="true" /><embed src="' . $content_info['href'] .'" width="550" height="400" controller="true" pluginspage="http://www.apple.com/quicktime/download/"></embed></object>';
873
874                 /* Oct 19, 2009
875                  * commenting this whole chunk out.  It's part of my test import codes, not sure why it's here, 
876                  * and I don't think it should be here.  Remove this whole comment after further testing and confirmation.
877                  * @harris
878                  *
879                         //Mimic the array for now.
880                         $test_attributes['resource']['href'] = $test_xml_file;
881                         $test_attributes['resource']['type'] = isset($items[$item_id]['type'])?'imsqti_xmlv1p2':'imsqti_xmlv1p1';
882                         $test_attributes['resource']['file'] = $items[$item_id]['file'];
883 //                      $test_attributes['resource']['file'] = array($test_xml_file);
884
885                         //Get the XML file out and start importing them into our database.
886                         //TODO: See question_import.php 287-289.
887                         $qids = $qti_import->importQuestions($test_attributes);
888                 
889                  */
890                 } else if ($ext == 'mp3') {
891                         $content = '<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" width="200" height="15" codebase="http://www.apple.com/qtactivex/qtplugin.cab"><param name="src" value="'. $content_info['href'] . '" /><param name="autoplay" value="false" /><embed src="' . $content_info['href'] .'" width="200" height="15" autoplay="false" pluginspage="http://www.apple.com/quicktime/download/"></embed></object>';
892                 } else if (in_array($ext, array('wav', 'au'))) {
893                         $content = '<embed SRC="'.$content_info['href'].'" autostart="false" width="145" height="60"><noembed><bgsound src="'.$content_info['href'].'"></noembed></embed>';
894
895                 } else if (in_array($ext, array('txt', 'css', 'html', 'htm', 'csv', 'asc', 'tsv', 'xml', 'xsl'))) {
896                         /* this is a plain text file */
897                         $content = file_get_contents($import_path.$content_info['href']);
898                         if ($content === false) {
899                                 /* if we can't stat() it then we're unlikely to be able to read it */
900                                 /* so we'll never get here. */
901                                 continue;
902                         }
903
904                         // get the contents of the 'head' element
905                         $head .= ContentUtility::getHtmlHeadByTag($content, $html_head_tags);
906                         
907                         // Specifically handle eXe package
908                         // NOTE: THIS NEEDS WORK! TO FIND A WAY APPLY EXE .CSS FILES ONLY ON COURSE CONTENT PART.
909                         // NOW USE OUR OWN .CSS CREATED SOLELY FOR EXE
910                         $isExeContent = false;
911
912                         // check xml file in eXe package
913                         if (preg_match("/<organization[ ]*identifier=\"eXe*>*/", $ims_manifest_xml))
914                         {
915                                 $isExeContent = true;
916                         }
917
918                         // use ATutor's eXe style sheet as the ones from eXe conflicts with ATutor's style sheets
919                         if ($isExeContent)
920                         {
921                                 $head = preg_replace ('/(<style.*>)(.*)(<\/style>)/ms', '\\1@import url(/docs/exestyles.css);\\3', $head);
922                         }
923
924                         // end of specifically handle eXe package
925
926                         $content = ContentUtility::getHtmlBody($content);
927                         if ($contains_glossary_terms) 
928                         {
929                                 // replace glossary content package links to real glossary mark-up using [?] [/?]
930                                 // refer to bug 3641, edited by Harris
931                                 $content = preg_replace('/<a href="([.\w\d\s]+[^"]+)" target="body" class="at-term">([.\w\d\s&;"]+|.*)<\/a>/i', '[?]\\2[/?]', $content);
932                         }
933
934                         /* potential security risk? */
935                         if ( strpos($content_info['href'], '..') === false && !preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $content_info['href'])) {
936 //                              @unlink($import_path.$content_info['href']);
937                         }
938                 } else if ($ext) {
939                         /* non text file, and can't embed (example: PDF files) */
940                         $content = '<a href="'.$content_info['href'].'">'.$content_info['title'].'</a>';
941                 }       
942         }
943         $content_parent_id = $cid;
944         if ($content_info['parent_content_id'] !== 0) {
945                 $content_parent_id = $items[$content_info['parent_content_id']]['real_content_id'];
946                 //if it's not there, use $cid
947                 if (!$content_parent_id){
948                         $content_parent_id = $cid;
949                 }
950         }
951
952         $my_offset = 0;
953         if ($content_parent_id == $cid) {
954                 $my_offset = $order_offset;
955         }
956
957         /* replace the old path greatest common denomiator with the new package path. */
958         /* we don't use str_replace, b/c there's no knowing what the paths may be         */
959         /* we only want to replace the first part of the path.  
960         */
961         if ($package_base_path != '') {
962                 $content_info['new_path'] = $package_base_name . substr($content_info['new_path'], strlen($package_base_path));
963         } else {
964                 $content_info['new_path'] = $package_base_name . '/' . $content_info['new_path'];
965         }
966
967         //handles weblinks
968         if ($content_info['type']=='imswl_xmlv1p0'){
969                 $weblinks_parser = new WeblinksParser();
970                 $xml_content = @file_get_contents($import_path . $content_info['href']);
971                 $weblinks_parser->parse($xml_content);
972                 $content_info['title'] = $weblinks_parser->getTitle();
973                 $content = $weblinks_parser->getUrl();
974                 $content_folder_type = CONTENT_TYPE_WEBLINK;
975                 $content_formatting = 2;
976         }
977         $head = addslashes($head);
978         $content_info['title'] = addslashes($content_info['title']);
979         $content_info['test_message'] = addslashes($content_info['test_message']);
980
981         //if this file is a test_xml, create a blank page instead, for imscc.
982         if (preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $content_info['href']) 
983                 || preg_match('/imsqti\_(.*)/', $content_info['type'])) {
984                 $content = '';
985         } else {
986                 $content = addslashes($content);
987         }
988
989         //check for content_type
990         if ($content_formatting!=CONTENT_TYPE_WEBLINK){
991                 $content_folder_type = ($content==''?CONTENT_TYPE_FOLDER:CONTENT_TYPE_CONTENT);
992         }
993
994         $items[$item_id]['real_content_id'] = $contentDAO->Create($course_id, intval($content_parent_id), 
995                             ($content_info['ordering'] + $my_offset - $lti_offset[$content_info['parent_content_id']] + 1),
996                             $last_modified, 0, $content_formatting, "", $content_info['new_path'], $content_info['title'],
997                             $content, $head, 1, $content_info['test_message'], 0, $content_folder_type);
998 //      $sql= 'INSERT INTO '.TABLE_PREFIX.'content'
999 //            . '(course_id, 
1000 //                content_parent_id, 
1001 //                ordering,
1002 //                last_modified, 
1003 //                revision, 
1004 //                formatting, 
1005 //                head,
1006 //                use_customized_head,
1007 //                keywords, 
1008 //                content_path, 
1009 //                title, 
1010 //                text,
1011 //                        test_message,
1012 //                        content_type) 
1013 //             VALUES 
1014 //                           ('.$_SESSION['course_id'].','                                                                                                                      
1015 //                           .intval($content_parent_id).','            
1016 //                           .($content_info['ordering'] + $my_offset - $lti_offset[$content_info['parent_content_id']] + 1).','
1017 //                           .'"'.$last_modified.'",                                                                                                    
1018 //                            0,'
1019 //                           .$content_formatting.' ,"'
1020 //                           . $head .'",
1021 //                           1,
1022 //                            "",'
1023 //                           .'"'.$content_info['new_path'].'",'
1024 //                           .'"'.$content_info['title'].'",'
1025 //                           .'"'.$content.'",'
1026 //                               .'"'.$content_info['test_message'].'",'
1027 //                               .$content_folder_type.')';
1028 //
1029 //      $result = mysql_query($sql, $db) or die(mysql_error());
1030
1031         /* get the content id and update $items */
1032 //      $items[$item_id]['real_content_id'] = mysql_insert_id($db);
1033
1034         /* get the tests associated with this content */
1035         if (!empty($items[$item_id]['tests']) || strpos($items[$item_id]['type'], 'imsqti_xmlv1p2/imscc_xmlv1p0') !== false){
1036                 $qti_import = new QTIImport($import_path);
1037
1038                 if (isset($items[$item_id]['tests'])){
1039                         $loop_var = $items[$item_id]['tests'];
1040                 } else {
1041                         $loop_var = $items[$item_id]['file'];
1042                 }
1043
1044                 foreach ($loop_var as $array_id => $test_xml_file){
1045                         //call subrountine to add the questions.
1046                         $qids = addQuestions($test_xml_file, $items[$item_id], $import_path);
1047                         
1048                         //import test
1049                         $tid = $qti_import->importTest($content_info['title']);
1050
1051                         //associate question and tests
1052                         foreach ($qids as $order=>$qid){
1053                                 if (isset($qti_import->weights[$order])){
1054                                         $weight = round($qti_import->weights[$order]);
1055                                 } else {
1056                                         $weight = 0;
1057                                 }
1058                                 $new_order = $order + 1;
1059                                 $sql = "INSERT INTO " . TABLE_PREFIX . "tests_questions_assoc" . 
1060                                                 "(test_id, question_id, weight, ordering, required) " .
1061                                                 "VALUES ($tid, $qid, $weight, $new_order, 0)";
1062                                 $result = mysql_query($sql, $db);
1063                         }
1064
1065                         //associate content and test
1066                         $sql =  'INSERT INTO ' . TABLE_PREFIX . 'content_tests_assoc' . 
1067                                         '(content_id, test_id) ' .
1068                                         'VALUES (' . $items[$item_id]['real_content_id'] . ", $tid)";
1069                         $result = mysql_query($sql, $db);
1070                 
1071 //                      if (!$msg->containsErrors()) {
1072 //                              $msg->addFeedback('IMPORT_SUCCEEDED');
1073 //                      }
1074                 }
1075         }
1076
1077         /* get the a4a related xml */
1078         if (isset($items[$item_id]['a4a_import_enabled']) && isset($items[$item_id]['a4a']) && !empty($items[$item_id]['a4a'])) {
1079                 $a4a_import = new A4aImport($items[$item_id]['real_content_id']);
1080                 $a4a_import->setRelativePath($items[$item_id]['new_path']);
1081                 $a4a_import->importA4a($items[$item_id]['a4a']);
1082         }
1083 }
1084
1085 if ($package_base_path == '.') {
1086         $package_base_path = '';
1087 }
1088
1089 // loop through the files outside the package folder, and copy them to its relative path
1090 if (is_dir(TR_TEMP_DIR . 'import/'.$course_id.'/resources')) {
1091         $handler = opendir(TR_TEMP_DIR . 'import/'.$course_id.'/resources');
1092         while ($file = readdir($handler)){
1093                 $filename = TR_TEMP_DIR . 'import/'.$course_id.'/resources/'.$file;
1094                 if(is_file($filename)){
1095                         @rename($filename, TR_TEMP_DIR .$course_id.'/'.$package_base_name.'/'.$file);
1096                 }
1097         }
1098         closedir($handler);
1099 }
1100
1101 $course_dir = TR_TEMP_DIR.$course_id.'/';
1102
1103 if (!is_dir($course_dir)) {
1104         if (!@mkdir($course_dir, 0700)) {
1105                 $msg->addError('IMPORTDIR_FAILED');
1106         }
1107 }
1108
1109
1110 if (@rename($import_path.$package_base_path, $course_dir.$package_base_name) === false) {
1111         if (!$msg->containsErrors()) {
1112                 $msg->addError('IMPORT_FAILED');
1113         }
1114 }
1115 //check if there are still resources missing
1116 foreach($items as $idetails){
1117         $temp_path = pathinfo($idetails['href']);
1118         @rename($import_path.$temp_path['dirname'], $course_dir.$package_base_name . '/' . $temp_path['dirname']);
1119 }
1120 clr_dir($import_path);
1121
1122 if (isset($_POST['url'])) {
1123         @unlink($full_filename);
1124 }
1125
1126
1127 //if ($_POST['s_cid'] || $course_id){
1128         if (!$msg->containsErrors()) {
1129                 $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY');
1130         }
1131         header('Location: ../course.php?cid='.$course_id);
1132         exit;
1133 //} else {
1134 //      if (!$msg->containsErrors()) {
1135 //              $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY');
1136 //      }
1137 //      if ($_GET['tile']) {
1138 //              header('Location: '.TR_BASE_HREF.'tools/tile/index.php');
1139 //      } else {
1140 //              header('Location: ../course.php?cid='.$course_id);
1141 //      }
1142 //      exit;
1143 //}
1144
1145 ?>