AT-4848: Added a condition to handle a false positive.
[atutor.git] / docs / mods / _core / imscp / ims_import.php
1 <?php
2 /************************************************************************/
3 /* ATutor                                                               */
4 /************************************************************************/
5 /* Copyright (c) 2002 - 2009                                            */
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 // $Id$
13 define('AT_INCLUDE_PATH', '../../../include/');
14 require(AT_INCLUDE_PATH.'vitals.inc.php');
15
16 require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); /* for clr_dir() and preImportCallBack and dirsize() */
17 require(AT_INCLUDE_PATH.'classes/pclzip.lib.php');
18 require(AT_INCLUDE_PATH.'../mods/_core/imsqti/lib/qti.inc.php'); 
19 require(AT_INCLUDE_PATH.'../mods/_core/imsqti/classes/QTIImport.class.php');
20 require(AT_INCLUDE_PATH.'../mods/_core/imsafa/classes/A4aImport.class.php');
21 require(AT_INCLUDE_PATH.'../mods/_core/imscp/ns.inc.php');      //namespace, no longer needs, delete it after it's stable.
22 require(AT_INCLUDE_PATH.'../mods/_core/imscc/classes/WeblinksParser.class.php');
23 require(AT_INCLUDE_PATH.'../mods/_core/imscc/classes/DiscussionToolsParser.class.php');
24 require(AT_INCLUDE_PATH.'../mods/_core/imscc/classes/DiscussionToolsImport.class.php');
25
26
27 /* make sure we own this course that we're exporting */
28 authenticate(AT_PRIV_CONTENT);
29
30 /* to avoid timing out on large files */
31 @set_time_limit(0);
32 $_SESSION['done'] = 1;
33
34 $html_head_tags = array("style", "script", "link");
35
36 $package_base_path = '';
37 $package_real_base_path = '';   //the path to save the contents
38 $all_package_base_path = array();
39 $xml_base_path = '';
40 $element_path = array();
41 $imported_glossary = array();
42 $character_data = '';
43 $test_message = '';
44 $test_title = '';
45 $content_type = '';
46 $skip_ims_validation = false;
47 $added_dt = array();    //the mapping of discussion tools that are added
48 $avail_dt = array();    //list of discussion tools that have not been handled
49
50 /*
51  * return the error messages represented by the given array 
52  * @author      Mike A.
53  * @ref         http://ca3.php.net/manual/en/domdocument.schemavalidate.php
54  */
55 function libxml_display_error($error)
56 {
57     $return = "<br/>\n";
58     switch ($error->level) {
59         case LIBXML_ERR_WARNING:
60             $return .= "<b>Warning $error->code</b>: ";
61             break;
62         case LIBXML_ERR_ERROR:
63             $return .= "<b>Error $error->code</b>: ";
64             break;
65         case LIBXML_ERR_FATAL:
66             $return .= "<b>Fatal Error $error->code</b>: ";
67             break;
68     }
69     $return .= trim($error->message);
70     if ($error->file) {
71         $return .=    " in <b>$error->file</b>";
72     }
73     $return .= " on line <b>$error->line</b>\n";
74
75     return $return;
76 }
77
78 /**
79  * Validate all the XML in the package, including checking XSDs, missing data.
80  * @param       string          the path of the directory that contains all the package files
81  * @return      boolean         true if every file exists in the manifest, false if any is missing.
82  */
83 function checkResources($import_path){
84         global $items, $msg, $skip_ims_validation, $avail_dt;
85
86         if (!is_dir($import_path)){
87                 return;
88         }
89
90         //if the package has access for all content, skip validation for now. 
91         //todo: import the XSD into our validator
92         if ($skip_ims_validation){
93                 return true;
94         }
95
96         //generate a file tree
97         $data = rscandir($import_path);
98         //check if every file is presented in the manifest
99         foreach($data as $filepath){
100 //      debug(preg_match('/(.*)\.xml/', substr($filepath, strlen($import_path))));
101                 //validate xml via its xsd/dtds
102                 if (preg_match('/(.*)\.xml/', substr($filepath, strlen($import_path)))){
103                         libxml_use_internal_errors(true);
104                         $dom = new DOMDocument();
105                         $dom->load(realpath($filepath));
106 //                      debug(realpath($filepath), 'filepath');
107                         if (!$dom->schemaValidate('main.xsd')){
108                                 $errors = libxml_get_errors();
109 //                              debug($errors);exit;
110                                 foreach ($errors as $error) {
111                                         //suppress warnings
112                                         if ($error->level==LIBXML_ERR_WARNING){
113                                                 continue;
114                                         }
115                                         $msg->addError(array('IMPORT_CARTRIDGE_FAILED', libxml_display_error($error)));
116                                 }
117                                 libxml_clear_errors();
118                         }
119                         
120                         //if this is the manifest file, we do not have to check for its existance.
121 //                      if (preg_match('/(.*)imsmanifest\.xml/', $filepath)){
122 //                              continue;
123 //                      }
124                 }
125         }
126
127         //Create an array that mimics the structure of the data array, based on the xml items
128         $filearray = array();
129         foreach($items as $name=>$fileinfo){
130                 if(isset($fileinfo['file']) && is_array($fileinfo['file']) && !empty($fileinfo['file'])){
131                         foreach($fileinfo['file'] as $fn){
132                                 if (!in_array(realpath($import_path.$fn), $filearray)){
133                                         //if url, skip
134                                         if (preg_match('/^http[s]?\:/', $fn) == 0){
135                                                 $filearray[] = realpath($import_path. $fn);
136                                         }                                       
137                                 }
138                         }
139                 }
140
141                 //validate the xml by its schema
142                 if (preg_match('/imsqti\_(.*)/', $fileinfo['type'])){
143                         $qti = new QTIParser($fileinfo['type']);
144                         $xml_content = @file_get_contents($import_path . $fileinfo['href']);
145                         $qti->parse($xml_content); //will add error to $msg if failed                   
146                 } 
147
148                 //add all dependent discussion tools to a list
149                 if(isset($fileinfo['dependency']) && !empty($fileinfo['dependency'])){
150                         $avail_dt = array_merge($avail_dt, $fileinfo['dependency']);
151                 }
152         }
153
154         //check if all files in the xml is presented in the archieve
155         $result = array_diff($filearray, $data);
156         //using sizeof because array_diff only 
157         //returns an array containing all the entries from array1  that are not present in any of the 
158         //other arrays. 
159         //Using sizeof make sure it's not a subset of array2.
160         //-1 on data because it always contain the imsmanifest.xml file
161         if (!$skip_ims_validation){
162             if (!empty($result) || sizeof($data)-1>sizeof($filearray)){
163                     $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('ims_missing_references')));
164             }
165         }
166         return true;
167 }
168
169 /*
170  * @example rscandir(dirname(__FILE__).'/'));
171  * @param string $base
172  * @param array $omit
173  * @param array $data
174  * @return array
175  */
176 function rscandir($base='', &$data=array()) {
177   $array = array_diff(scandir($base), array('.', '..')); # remove ' and .. from the array */
178   foreach($array as $value) : /* loop through the array at the level of the supplied $base */
179  
180     if (is_dir($base.$value)) : /* if this is a directory */
181 //        don't save the directory name
182 //        $data[] = $base.$value.'/'; /* add it to the $data array */
183       $data = rscandir($base.$value.'/', $data); /* then make a recursive call with the
184       current $value as the $base supplying the $data array to carry into the recursion */
185      
186     elseif (is_file($base.$value)) : /* else if the current $value is a file */
187       $data[] = realpath($base.$value); /* just add the current $value to the $data array */
188      
189     endif;
190    
191   endforeach;
192   return $data; // return the $data array
193  
194 }
195
196 /**
197  * Function to restructure the $items.  So that old import will merge the top page into its children, and
198  * create a new folder on top of it
199  */
200 function rehash($items){
201         global $order;
202         $parent_page_maps = array();    //old=>new
203         $temp_popped_items = array();
204         $rehashed_items = array();      //the reconstructed array
205         foreach($items as $id => $content){
206                 $parent_obj = $items[$content['parent_content_id']];
207                 $rehashed_items[$id] = $content;        //copy
208         //first check if this is the top folder of the archieve, we don't want the top folder, remove it.
209 /*        if (isset($content['parent_content_id']) && !isset($parent_obj) && !isset($content['type'])){
210             //if we can get into here, it means the parent_content_id of this is empty
211             //implying this is the first folder.
212             //note: it checks content[type] cause it could be a webcontent. In that case, 
213             //      we do want to keep it.  
214                         debug($content, 'hit');
215             unset($rehashed_items[$id]);
216             continue;
217         }               
218                 //then check if there exists a mapping for this item, if so, simply replace is and next.
219                 else
220 */              if (isset($parent_page_maps[$content['parent_content_id']])){
221                         $rehashed_items [$id]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']];
222                         $rehashed_items [$id]['ordering']++;
223                 } 
224                 //If its parent page is a top page and have an identiferref
225                 elseif (isset($parent_obj) && isset($parent_obj['href'])){                      
226                         if (!isset($parent_obj['href'])){
227                                 //check if this top page is already a folder, if so, next.
228                                 continue;
229                         }
230                         //else, make its parent page to a folder
231                         $new_item['title'] = $parent_obj['title'];
232                         //check if this parent has been modified, if so, chnage it
233                         if (isset($parent_page_maps[$parent_obj['parent_content_id']])){
234                             $new_item['parent_content_id'] = $parent_page_maps[$parent_obj['parent_content_id']];
235                         } else {
236                         $new_item['parent_content_id'] = $parent_obj['parent_content_id'];
237             }
238                         //all ordering needs to be +1 because we are creating a new folder on top of
239                         //everything, except the first page.
240                         $new_item['ordering'] = $parent_obj['ordering'];
241                         if ($new_item['parent_content_id']!='0'){
242                                 $new_item['ordering']++;
243                         } 
244
245                 //assign this new parent folder to the pending items array
246                         $new_item_name = $content['parent_content_id'].'_FOLDER';
247                         //a not so brilliant way to append the folder in its appropriate position
248                         $reordered_hashed_items = array();  //use to store the new rehashed item with the correct item order
249                         foreach($rehashed_items as $rh_id=>$rh_content){
250                             if ($rh_id == $content['parent_content_id']){
251                                 //add the folder in before the parent subpage.
252                                 $reordered_hashed_items[$new_item_name] = $new_item;
253                             }
254                             $reordered_hashed_items[$rh_id] = $rh_content;  //clone
255                         }
256                         $rehashed_items = $reordered_hashed_items;  //replace it back
257                         unset($reordered_hashed_items);
258                         $parent_page_maps[$content['parent_content_id']] = $new_item_name;  //save this page on the hash map
259
260                         //reconstruct the parent
261                         $rehashed_items[$content['parent_content_id']]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']];
262                         $rehashed_items[$content['parent_content_id']]['ordering'] = 0; //always the first one.
263
264                         //reconstruct itself
265                         $rehashed_items[$id]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']];
266                         $rehashed_items[$id]['ordering']++;
267
268                 }
269         }
270         return $rehashed_items;
271 }
272
273 /**
274  * Take out the common path within all $items['new_path']. Make sure we include
275  * AccessForAll files in the array.  
276  * This allows import/export repeatedly without duplicating its path
277  * @param   array   contains the breakdown of all resources in the XML
278  */
279 function removeCommonPath($items){
280     $common_path; 
281     $quit = false;  //a flag that is set if it's not the first time being run.
282
283     $filearray = array();
284     //get all files listed in the manifest
285     foreach($items as $name=>$fileinfo){
286                 if(isset($fileinfo['file']) && is_array($fileinfo['file']) && !empty($fileinfo['file'])){
287                         foreach($fileinfo['file'] as $fn){
288                                 if (!in_array($fn, $filearray)){
289                                         if (preg_match('/^http[s]?\:/', $fn) == 0){
290                                                 $filearray[] = $fn;
291                                         }                                       
292                                 }
293                         }
294                 }
295         }
296
297     foreach($filearray as $index=>$path){
298         //hack
299         //check if this is a XML file; if so, skip through, 
300         //cause XML most likely isn't a content resource.
301         $ext = substr($path, (strrpos($path, '.')+1));
302         if($ext=='xml'){
303             continue;
304         }
305         //if common path is empty, assign the first path to it.
306         if ($common_path=='' && $quit==false){
307             $common_path = $path;
308             $quit = true;   //the next time common_path is empty, quit;
309             continue;
310         }
311         //we use '/' here instead of DIRECTORY_SEPARATOR because php would
312         //actually use '\' and return the whole string. 
313         $common_array = explode('/', $common_path);
314         $path_array = explode('/', $path);
315         //convert path_array to absolute path
316         //TODO: array_search is slow, build a faster search
317         $pos=array_search('..', $path_array);
318         while($pos=array_search('..', $path_array)){
319             array_splice($path_array, $pos-1, 2);
320         }
321         $intersect_array = array_intersect($common_array, $path_array);
322         $common_path = implode('/', $intersect_array);       
323     }
324     // If this path (including file extension) is in the filearray,
325     // then this means there is only 1 file in the array, implies there
326     // wasn't any intersection at all.  In this case, use the base root.
327     if(in_array($common_path, $filearray)) {
328         $common_path = '';
329     }
330     return $common_path;
331 }
332
333
334 /** 
335  * This function will take the test accessment XML and add these to the database.
336  * @param       string  The path of the XML, without the import_path.
337  * @param       mixed   An item singleton.  Contains the info of this item, namely, the accessment details.
338  *                                      The item must be an object created by the ims class.
339  * @param       string  the import path
340  * @return      mixed   An Array that contains all the question IDs that have been imported.
341  */
342  function addQuestions($xml, $item, $import_path){
343         global $test_title;
344         $qti_import = new QTIImport($import_path);
345         $tests_xml = $import_path.$xml;
346         
347         //Mimic the array for now.
348         $test_attributes['resource']['href'] = $item['href'];
349         $test_attributes['resource']['type'] = preg_match('/imsqti_xmlv1p2/', $item['type'])==1?'imsqti_xmlv1p2':'imsqti_xmlv1p1';
350         $test_attributes['resource']['file'] = $item['file'];
351
352         //Get the XML file out and start importing them into our database.
353         //TODO: See question_import.php 287-289.
354         $qids = $qti_import->importQuestions($test_attributes);
355         $test_title = $qti_import->title;
356
357         return $qids;
358  }
359
360
361         /* called at the start of en element */
362         /* builds the $path array which is the path from the root to the current element */
363         function startElement($parser, $name, $attrs) {
364                 global $items, $path, $package_base_path, $all_package_base_path, $package_real_base_path;
365                 global $element_path, $import_path, $skip_ims_validation;
366                 global $xml_base_path, $test_message, $content_type;
367                 global $current_identifier, $msg, $ns, $ns_cp;
368
369                 //check if the xml is valid
370 /*
371                 if(isset($attrs['xsi:schemaLocation']) && $name == 'manifest'){
372                         //run the loop and check it thru the ns.inc.php
373                 } elseif ($name == 'manifest' && !isset($attrs['xsi:schemaLocation'])) {
374                         //$msg->addError('MANIFEST_NOT_WELLFORM: NO NAMESPACE');
375                         $msg->addError('IMPORT_CARTRIDGE_FAILED');
376                 } else {
377                         //error
378                 }
379                 //error if the tag names are wrong
380                 if (preg_match('/^xsi\:/', $name) >= 1){
381                         //$msg->addError('MANIFEST_NOT_WELLFORM');
382                         $msg->addError('IMPORT_CARTRIDGE_FAILED');
383                 }
384 */
385
386                 //validate namespaces
387                 if(!$skip_ims_validation && isset($attrs['xsi:schemaLocation']) && $name=='manifest'){
388                         $schema_location = array();
389                         $split_location = preg_split('/[\r\n\s]+/', trim($attrs['xsi:schemaLocation']));
390
391                         //check if the namespace is actually right, have an array or some sort in IMS class
392                         if(sizeof($split_location)%2==1){
393                                 //schema is not in the form of "The first URI reference in each pair is a namespace name,
394                                 //and the second is the location of a schema that describes that namespace."
395                                 //$msg->addError('MANIFEST_NOT_WELLFORM');
396                                 $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('schema_error')));
397                         }
398
399                         //turn the xsi:schemaLocation URI into a schema that describe namespace.
400                         //name = url
401                         //http://msdn.microsoft.com/en-us/library/ms256100(VS.85).aspx
402                         //http://www.w3.org/TR/xmlschema-1/
403                         for($i=0; $i < sizeof($split_location);$i=$i+2){
404                                 /*
405                                 if (isset($ns[$split_location[$i]]) && $ns[$split_location[$i]] != $split_location[$i+1]){
406                                         //$msg->addError('MANIFEST_NOT_WELLFORM: SCHEMA');
407                                         $msg->addError('IMPORT_CARTRIDGE_FAILED');
408                                 }
409                                 */
410                                 //if the key of the namespace is not defined. Throw error.
411                                 if(!isset($ns[$split_location[$i]]) && !isset($ns_cp[$split_location[$i]])){
412                                         $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('schema_error')));
413                                 }
414                         }
415                 } else {
416                         //throw error           
417                 }
418
419                 if ($name == 'manifest' && isset($attrs['xml:base']) && $attrs['xml:base']) {
420                         $xml_base_path = $attrs['xml:base'];
421                 } else if ($name == 'file') {
422                         // check if it misses file references
423                         if(!$skip_ims_validation && (!isset($attrs['href']) || $attrs['href']=='')){
424                                 //$msg->addError('MANIFEST_NOT_WELLFORM');
425                                 $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('ims_missing_references')));
426                         }
427
428                         // special case for webCT content packages that don't specify the `href` attribute 
429                         // with the `<resource>` element.
430                         // we take the `href` from the first `<file>` element.
431                         if (isset($items[$current_identifier]) && ($items[$current_identifier]['href'] == '')) {
432                                 $attrs['href'] = urldecode($attrs['href']);
433                                 $items[$current_identifier]['href'] = $attrs['href'];
434                         }
435
436                         $temp_path = pathinfo($attrs['href']);
437 //                      if (!strpos($temp_path['dirname'], 'Share')) {
438             if ($temp_path['extension'] == 'html') {
439                             $temp_path = explode('/', $temp_path['dirname']);
440                             if (empty($package_base_path)){
441                                 $package_base_path = $temp_path;
442                 }
443                             if ($all_package_base_path!='' && empty($all_package_base_path)){
444                                     $all_package_base_path = $temp_path;
445                             }
446                             $package_base_path = array_intersect_assoc($package_base_path, $temp_path);
447                         }
448
449                         //calculate the depths of relative paths
450                         if ($all_package_base_path!=''){
451                                 $no_relative_temp_path = $temp_path;
452                                 foreach($no_relative_temp_path as $path_node){
453                                         if ($path_node=='..'){
454                                                 array_pop($no_relative_temp_path);
455                                                 array_pop($no_relative_temp_path); //not a typo, have to pop twice, both itself('..'), and the one before.
456                                         }
457                                 }
458                                 $all_package_base_path = array_intersect_assoc($all_package_base_path, $no_relative_temp_path);
459                                 if (empty($all_package_base_path)){
460                                         $all_package_base_path = '';    //unset it, there is no intersection.
461                                 }
462                         }
463
464                         //save the actual content base path
465                         if (in_array('..', $temp_path)){
466                                 $sizeofrp = array_count_values($temp_path);
467                         }
468                         //for IMSCC, assume that all resources lies in the same folder, except styles.css               
469                         if ($items[$current_identifier]['type']=='webcontent' || $items[$current_identifier]['type']=='imsdt_xmlv1p0'){
470                                 //find the intersection of each item's related files, then that intersection is the content_path
471                                 if (isset($items[$current_identifier]['file'])){
472                                         foreach ($items[$current_identifier]['file'] as $resource_path){
473                                             if (!strpos($resource_path, 'Share')) {
474                                                     $temp_path = pathinfo($resource_path);
475                                                     $temp_path = explode('/', $temp_path['dirname']);
476
477                                                     $package_base_path = array_intersect_assoc($package_base_path, $temp_path);                                         
478                                                 }
479                                         }
480                                 }
481                         }
482
483                         //real content path
484                         if($sizeofrp['..'] > 0 && !empty($all_package_base_path)){
485                                 for ($i=0; $i<$sizeofrp['..']; $i++){
486                                         array_pop($all_package_base_path);
487                                 }
488                         }
489                         if (!empty($package_base_path)) {
490                                 $items[$current_identifier]['new_path'] = implode('/', $package_base_path);
491                         }
492                                 
493                         if (isset($_POST['allow_test_import']) && isset($items[$current_identifier]) 
494                                                 && preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $attrs['href'])) {
495                                 $items[$current_identifier]['tests'][] = $attrs['href'];
496                         } 
497                         if (isset($_POST['allow_a4a_import']) && isset($items[$current_identifier])) {
498                                 $items[$current_identifier]['a4a_import_enabled'] = true;
499                         }
500                 } else if (($name == 'item') && ($attrs['identifierref'] != '')) {
501                         $path[] = $attrs['identifierref'];
502                 } else if (($name == 'item') && ($attrs['identifier'])) {
503                         $path[] = $attrs['identifier'];
504 //              } else if (($name == 'resource') && is_array($items[$attrs['identifier']]))  {
505                 } else if (($name == 'resource')) {
506                         $current_identifier = $attrs['identifier'];
507                         $items[$current_identifier]['type'] = $attrs['type'];
508                         if ($attrs['href']) {
509                                 $attrs['href'] = urldecode($attrs['href']);
510
511                                 $items[$attrs['identifier']]['href'] = $attrs['href'];
512
513                                 // href points to a remote url
514                                 if (preg_match('/^http.*:\/\//', trim($attrs['href']))) {
515                                         $items[$attrs['identifier']]['new_path'] = '';
516                                 } else // href points to local file
517                                 {
518                                         $temp_path = pathinfo($attrs['href']);
519                                         $temp_path = explode('/', $temp_path['dirname']);
520 //                                      if (empty($package_base_path)) {
521                                                 $package_base_path = $temp_path;
522 //                                      } 
523 //                                      else {
524 //                                              $package_base_path = array_intersect($package_base_path, $temp_path);
525 //                                      }
526                                         $items[$attrs['identifier']]['new_path'] = implode('/', $temp_path);
527                                 }
528                         }
529
530                         //if test custom message has not been saved
531 //                      if (!isset($items[$current_identifier]['test_message'])){
532 //                              $items[$current_identifier]['test_message'] = $test_message;
533 //                      }
534                 } else if ($name=='dependency' && $attrs['identifierref']!='') {
535                         //if there is a dependency, attach it to the item array['file']
536                         $items[$current_identifier]['dependency'][] = $attrs['identifierref'];
537                 }
538                 if (($name == 'item') && ($attrs['parameters'] != '')) {
539                         $items[$attrs['identifierref']]['test_message'] = $attrs['parameters'];
540                 }
541                 if ($name=='file'){
542                         if(!isset($items[$current_identifier]) && $attrs['href']!=''){
543                                 $items[$current_identifier]['href']      = $attrs['href'];
544                         }
545                         if (substr($attrs['href'], 0, 7) == 'http://' || substr($attrs['href'], 0, 8) == 'https://' || file_exists($import_path.$attrs['href']) || $skip_ims_validation){
546                                 $items[$current_identifier]['file'][] = $attrs['href'];
547                         } else {
548                                 $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT(array('ims_files_missing', $attrs['href']))));
549                         }
550                 }               
551                 if ($name=='cc:authorizations'){
552                         //don't have authorization setup.
553                         //$msg->addError('');
554                         $msg->addError('IMS_AUTHORIZATION_NOT_SUPPORT');
555                 }
556                 array_push($element_path, $name);
557         }
558
559         /* called when an element ends */
560         /* removed the current element from the $path */
561         function endElement($parser, $name) {
562                 global $path, $element_path, $my_data, $items;
563                 global $current_identifier, $skip_ims_validation;
564                 global $msg, $content_type;             
565                 static $resource_num = 0;
566                 
567                 if ($name == 'item') {
568                         array_pop($path);
569                 } 
570
571                 //check if this is a test import
572                 if ($name == 'schema'){
573                         if (trim($my_data)=='IMS Question and Test Interoperability'){
574                                 $msg->addError('IMPORT_FAILED');
575                         } 
576                         $content_type = trim($my_data);
577                 }
578
579                 //Handles A4a
580                 if ($current_identifier != ''){
581                         $my_data = trim($my_data);
582                         $last_file_name = $items[$current_identifier]['file'][(sizeof($items[$current_identifier]['file']))-1];
583
584                         if ($name=='originalAccessMode'){                               
585                                 if (in_array('accessModeStatement', $element_path)){
586                                         $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['access_stmt_originalAccessMode'] = $my_data;
587                                 } elseif (in_array('adaptationStatement', $element_path)){
588                                         $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['adapt_stmt_originalAccessMode'] = $my_data;
589                                 }
590                         } elseif (($name=='language') && in_array('accessModeStatement', $element_path)){
591                                 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['language'][] = $my_data;
592                         } elseif ($name=='hasAdaptation') {
593                                 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['hasAdaptation'][] = $my_data;
594                         } elseif ($name=='isAdaptationOf'){
595                                 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['isAdaptationOf'][] = $my_data;
596                         } elseif ($name=='accessForAllResource'){
597                                 /* the head node of accessForAll Metadata, if this exists in the manifest. Skip XSD validation,
598                                  * because A4a doesn't have a xsd yet.  Our access for all is based on ISO which will not pass 
599                                  * the current IMS validation.  
600                                  * Also, since ATutor is the only one (as of Oct 21, 2009) that exports IMS with access for all
601                                  * content, we can almost assume that any ims access for all content is by us, and is valid. 
602                                  */
603                                 $skip_ims_validation = true;
604                                 $resource_num++;
605                         } elseif($name=='file'){
606                                 $resource_num = 0;      //reset resournce number to 0 when the file tags ends
607                         }
608                 }
609
610                 if ($element_path === array('manifest', 'metadata', 'imsmd:lom', 'imsmd:general', 'imsmd:title', 'imsmd:langstring')) {
611                         global $package_base_name;
612                         $package_base_name = trim($my_data);
613                 }
614
615                 array_pop($element_path);
616                 $my_data = '';
617         }
618
619         /* called when there is character data within elements */
620         /* constructs the $items array using the last entry in $path as the parent element */
621         function characterData($parser, $data){
622                 global $path, $items, $order, $my_data, $element_path;
623                 global $current_identifier;
624
625                 $str_trimmed_data = trim($data);
626                                 
627                 if (!empty($str_trimmed_data)) {
628                         $size = count($path);
629                         if ($size > 0) {
630                                 $current_item_id = $path[$size-1];
631                                 if ($size > 1) {
632                                         $parent_item_id = $path[$size-2];
633                                 } else {
634                                         $parent_item_id = 0;
635                                 }
636
637                                 if (isset($items[$current_item_id]['parent_content_id']) && is_array($items[$current_item_id])) {
638
639                                         /* this item already exists, append the title           */
640                                         /* this fixes {\n, \t, `, &} characters in elements */
641
642                                         /* horible kludge to fix the <ns2:objectiveDesc xmlns:ns2="http://www.utoronto.ca/atrc/tile/xsd/tile_objective"> */
643                                         /* from TILE */
644                                         if (in_array('accessForAllResource', $element_path)){
645                                                 //skip this tag
646                                         } elseif ($element_path[count($element_path)-1] != 'ns1:objectiveDesc') {
647                                                 $items[$current_item_id]['title'] .= $data;
648                                         }
649         
650                                 } else {
651                                         $order[$parent_item_id] ++;
652                                         $item_tmpl = array(     'title'                         => $data,
653                                                                                 'parent_content_id' => $parent_item_id,
654                                                                                 'ordering'                      => $order[$parent_item_id]-1);
655                                         //append other array values if it exists
656                                         if (is_array($items[$current_item_id])){
657                                                 $items[$current_item_id] = array_merge($items[$current_item_id], $item_tmpl);
658                                         } else {
659                                                 $items[$current_item_id] = $item_tmpl;
660                                         }
661                                 }
662                         }
663                 }
664
665                 $my_data .= $data;
666         }
667
668         /* glossary parser: */
669         function glossaryStartElement($parser, $name, $attrs) {
670                 global $element_path;
671
672                 array_push($element_path, $name);
673         }
674
675         /* called when an element ends */
676         /* removed the current element from the $path */
677         function glossaryEndElement($parser, $name) {
678                 global $element_path, $my_data, $imported_glossary;
679                 static $current_term;
680
681                 if ($element_path === array('glossary', 'item', 'term') || 
682                         $element_path === array('glossary:glossary', 'item', 'term')) {
683                         $current_term = $my_data;
684
685                 } else if ($element_path === array('glossary', 'item', 'definition') || 
686                                    $element_path === array('glossary:glossary', 'item', 'definition')) {
687                         $imported_glossary[trim($current_term)] = trim($my_data);
688                 }
689
690                 array_pop($element_path);
691                 $my_data = '';
692         }
693
694         function glossaryCharacterData($parser, $data){
695                 global $my_data;
696
697                 $my_data .= $data;
698         }
699
700 if (!isset($_POST['submit']) && !isset($_POST['cancel'])) {
701         /* just a catch all */
702         
703         $errors = array('FILE_MAX_SIZE', ini_get('post_max_size'));
704         $msg->addError($errors);
705
706         header('Location: ./index.php');
707         exit;
708 } else if (isset($_POST['cancel'])) {
709         $msg->addFeedback('IMPORT_CANCELLED');
710
711         header('Location: ../content/index.php');
712         exit;
713 }
714
715 $cid = intval($_POST['cid']);
716
717 //If user chooses to ignore validation.
718 if(isset($_POST['ignore_validation']) && $_POST['ignore_validation']==1) {
719         $skip_ims_validation = true;
720 }
721
722 if (isset($_POST['url']) && ($_POST['url'] != 'http://') ) {
723         if ($content = @file_get_contents($_POST['url'])) {
724                 // save file to /content/
725                 $filename = substr(time(), -6). '.zip';
726                 $full_filename = AT_CONTENT_DIR . $filename;
727
728                 if (!$fp = fopen($full_filename, 'w+b')) {
729                         echo "Cannot open file ($filename)";
730                         exit;
731                 }
732
733                 if (fwrite($fp, $content, strlen($content) ) === FALSE) {
734                         echo "Cannot write to file ($filename)";
735                         exit;
736                 }
737                 fclose($fp);
738         }       
739         $_FILES['file']['name']     = $filename;
740         $_FILES['file']['tmp_name'] = $full_filename;
741         $_FILES['file']['size']     = strlen($content);
742         unset($content);
743         //$url_parts = pathinfo($_POST['url']);
744         //$package_base_name_url = $url_parts['basename'];
745     $package_base_name_url = md5(time());
746 }
747 $ext = pathinfo($_FILES['file']['name']);
748 $ext = $ext['extension'];
749
750 if ($ext != 'zip') {
751         $msg->addError('IMPORTDIR_IMS_NOTVALID');
752 } else if ($_FILES['file']['error'] == 1) {
753         $errors = array('FILE_MAX_SIZE', ini_get('upload_max_filesize'));
754         $msg->addError($errors);
755 } else if ( !$_FILES['file']['name'] || (!is_uploaded_file($_FILES['file']['tmp_name']) && !$_POST['url'])) {
756         $msg->addError('FILE_NOT_SELECTED');
757 } else if ($_FILES['file']['size'] == 0) {
758         $msg->addError('IMPORTFILE_EMPTY');
759
760
761 if ($msg->containsErrors()) {
762         if (isset($_GET['tile'])) {
763                 header('Location: '.$_base_path.'mods/_standard/tile_search/index.php');
764         } else {
765                 header('Location: index.php');
766         }
767         exit;
768 }
769
770 /* check if ../content/import/ exists */
771 $import_path = AT_CONTENT_DIR . 'import/';
772 $content_path = AT_CONTENT_DIR;
773
774 if (!is_dir($import_path)) {
775         if (!@mkdir($import_path, 0700)) {
776                 $msg->addError('IMPORTDIR_FAILED');
777         }
778 }
779
780 $import_path .= $_SESSION['course_id'].'/';
781 if (is_dir($import_path)) {
782         clr_dir($import_path);
783 }
784
785 if (!@mkdir($import_path, 0700)) {
786         $msg->addError('IMPORTDIR_FAILED');
787 }
788
789 if ($msg->containsErrors()) {
790         if (isset($_GET['tile'])) {
791                 header('Location: '.$_base_path.'mods/_standard/tile_search/index.php');
792         } else {
793                 header('Location: index.php');
794         }
795         exit;
796 }
797
798 /* extract the entire archive into AT_COURSE_CONTENT . import/$course using the call back function to filter out php files */
799 error_reporting(0);
800 $archive = new PclZip($_FILES['file']['tmp_name']);
801 if ($archive->extract(  PCLZIP_OPT_PATH,        $import_path,
802                                                 PCLZIP_CB_PRE_EXTRACT,  'preImportCallBack') == 0) {
803         $msg->addError('IMPORT_FAILED');
804         echo 'Error : '.$archive->errorInfo(true);
805         clr_dir($import_path);
806         header('Location: index.php');
807         exit;
808 }
809 error_reporting(AT_ERROR_REPORTING);
810
811 /* get the course's max_quota */
812 $sql    = "SELECT max_quota FROM ".TABLE_PREFIX."courses WHERE course_id=$_SESSION[course_id]";
813 $result = mysql_query($sql, $db);
814 $q_row  = mysql_fetch_assoc($result);
815
816 if ($q_row['max_quota'] != AT_COURSESIZE_UNLIMITED) {
817
818         if ($q_row['max_quota'] == AT_COURSESIZE_DEFAULT) {
819                 $q_row['max_quota'] = $MaxCourseSize;
820         }
821         $totalBytes   = dirsize($import_path);
822         $course_total = dirsize(AT_CONTENT_DIR . $_SESSION['course_id'].'/');
823         $total_after  = $q_row['max_quota'] - $course_total - $totalBytes + $MaxCourseFloat;
824
825         if ($total_after < 0) {
826                 /* remove the content dir, since there's no space for it */
827                 $errors = array('NO_CONTENT_SPACE', number_format(-1*($total_after/AT_KBYTE_SIZE), 2 ) );
828                 $msg->addError($errors);
829                 
830                 clr_dir($import_path);
831
832                 if (isset($_GET['tile'])) {
833                         header('Location: '.$_base_path.'mods/_standard/tile_search/index.php');
834                 } else {
835                         header('Location: index.php');
836                 }
837                 exit;
838         }
839 }
840
841
842 $items = array(); /* all the content pages */
843 $order = array(); /* keeps track of the ordering for each content page */
844 $path  = array();  /* the hierarchy path taken in the menu to get to the current item in the manifest */
845 $dependency_files = array(); /* the file path for the dependency files */
846
847 /*
848 $items[content_id/resource_id] = array(
849                                                                         'title'
850                                                                         'real_content_id' // calculated after being inserted
851                                                                         'parent_content_id'
852                                                                         'href'
853                                                                         'ordering'
854                                                                         );
855 */
856 $ims_manifest_xml = @file_get_contents($import_path.'imsmanifest.xml');
857 //scan for manifest xml if it's not on the top level.
858 if ($ims_manifest_xml === false){
859         $data = rscandir($import_path);
860         $manifest_array = array();
861         foreach($data as $scanned_file){
862                 $scanned_file = realpath($scanned_file);
863                 //change the file string to an array
864                 $this_file_array = explode(DIRECTORY_SEPARATOR, $scanned_file);
865                 if(empty($manifest_array)){
866                         $manifest_array = $this_file_array;
867                 }
868                 $manifest_array = array_intersect_assoc($this_file_array, $manifest_array);
869
870                 if (strpos($scanned_file, 'imsmanifest')!==false){
871                         $ims_manifest_xml = @file_get_contents($scanned_file);
872                 }
873         }
874         if ($ims_manifest_xml !== false){
875                 $import_path = implode(DIRECTORY_SEPARATOR, $manifest_array) . DIRECTORY_SEPARATOR;
876         }
877 }
878
879 //if no imsmanifest.xml found in the entire package, throw error.
880 if ($ims_manifest_xml === false) {
881         $msg->addError('NO_IMSMANIFEST');
882
883         if (file_exists($import_path . 'atutor_backup_version')) {
884                 $msg->addError('NO_IMS_BACKUP');
885         }
886         clr_dir($import_path);
887
888         if (isset($_GET['tile'])) {
889                 header('Location: '.$_base_path.'mods/_standard/tile_search/index.php');
890         } else {
891                 header('Location: index.php');
892         }
893         exit;
894 }
895
896 $xml_parser = xml_parser_create();
897
898 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */
899 xml_set_element_handler($xml_parser, 'startElement', 'endElement');
900 xml_set_character_data_handler($xml_parser, 'characterData');
901
902 if (!xml_parse($xml_parser, $ims_manifest_xml, true)) {
903         die(sprintf("XML error: %s at line %d",
904                                 xml_error_string(xml_get_error_code($xml_parser)),
905                                 xml_get_current_line_number($xml_parser)));
906 }
907 xml_parser_free($xml_parser);
908 /* check if the glossary terms exist */
909 $glossary_path = '';
910 if ($content_type == 'IMS Common Cartridge'){
911         $glossary_path = 'resources/GlossaryItem/';
912 //      $package_base_path = '';
913 }
914 if (file_exists($import_path . $glossary_path . 'glossary.xml')){
915         $glossary_xml = @file_get_contents($import_path.$glossary_path.'glossary.xml');
916         $element_path = array();
917         $xml_parser = xml_parser_create();
918
919         /* insert the glossary terms into the database (if they're not in there already) */
920         /* parse the glossary.xml file and insert the terms */
921         xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */
922         xml_set_element_handler($xml_parser, 'glossaryStartElement', 'glossaryEndElement');
923         xml_set_character_data_handler($xml_parser, 'glossaryCharacterData');
924
925         if (!xml_parse($xml_parser, $glossary_xml, true)) {
926                 die(sprintf("XML error: %s at line %d",
927                                         xml_error_string(xml_get_error_code($xml_parser)),
928                                         xml_get_current_line_number($xml_parser)));
929         }
930         xml_parser_free($xml_parser);
931         $contains_glossary_terms = true;
932         foreach ($imported_glossary as $term => $defn) {
933                 if (!$glossary[$term]) {
934                         $sql = "INSERT INTO ".TABLE_PREFIX."glossary VALUES (NULL, $_SESSION[course_id], '$term', '$defn', 0)";
935                         mysql_query($sql, $db); 
936                 }
937         }
938 }
939
940 // Check if all the files exists in the manifest, iff it's a IMS CC package.
941 if ($content_type == 'IMS Common Cartridge') {  
942         checkResources($import_path);
943 }
944
945 // Check if there are any errors during parsing.
946 if ($msg->containsErrors()) {
947         if (isset($_GET['tile'])) {
948                 header('Location: '.$_base_path.'mods/_standard/tile_search/index.php');
949         } else {
950                 header('Location: index.php');
951         }
952         exit;
953 }
954
955 /* generate a unique new package base path based on the package file name and date as needed. */
956 /* the package name will be the dir where the content for this package will be put, as a result */
957 /* the 'content_path' field in the content table will be set to this path. */
958 /* $package_base_name_url comes from the URL file name (NOT the file name of the actual file we open)*/
959 if (!$package_base_name && $package_base_name_url) {
960         $package_base_name = substr($package_base_name_url, -6);
961 } else if (!$package_base_name) {
962         $package_base_name = substr($_FILES['file']['name'], 0, -4);
963 }
964
965 $package_base_name = strtolower($package_base_name);
966 $package_base_name = str_replace(array('\'', '"', ' ', '|', '\\', '/', '<', '>', ':'), '_' , $package_base_name);
967 $package_base_name = preg_replace("/[^A-Za-z0-9._\-]/", '', $package_base_name);
968
969 if (is_dir(AT_CONTENT_DIR . $_SESSION['course_id'].'/'.$package_base_name)) {
970         $package_base_name .= '_'.date('ymdHis');
971 }
972
973 if ($package_base_path) {
974         $package_base_path = implode('/', $package_base_path);
975 } elseif (empty($package_base_path)){
976         $package_base_path = '';
977 }
978 if ($xml_base_path) {
979         $package_base_path = $xml_base_path . $package_base_path;
980
981         mkdir(AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$xml_base_path);
982         $package_base_name = $xml_base_path . $package_base_name;
983 }
984
985 /* get the top level content ordering offset */
986 $sql    = "SELECT MAX(ordering) AS ordering FROM ".TABLE_PREFIX."content WHERE course_id=$_SESSION[course_id] AND content_parent_id=$cid";
987 $result = mysql_query($sql, $db);
988 $row    = mysql_fetch_assoc($result);
989 $order_offset = intval($row['ordering']); /* it's nice to have a real number to deal with */
990 $lti_offset = array();  //since we don't need lti tools, the ordering needs to be subtracted
991 //reorder the items stack
992 $common_path = removeCommonPath($items);
993 $items = rehash($items);
994 //debug($items);exit;
995 foreach ($items as $item_id => $content_info) 
996 {       
997         //formatting field, default 1
998         $content_formatting = 1;        //CONTENT_TYPE_CONTENT
999
1000         //don't want to display glossary as a page
1001         if ($content_info['href']== $glossary_path . 'glossary.xml'){
1002                 continue;
1003         }
1004
1005         //if discussion tools, add it to the list of unhandled dts
1006         if ($content_info['type']=='imsdt_xmlv1p0'){
1007                 //if it will be taken care after (has dependency), then move along.
1008                 if (in_array($item_id, $avail_dt)){
1009                         $lti_offset[$content_info['parent_content_id']]++;
1010                         continue;
1011                 }
1012         }
1013
1014         //handle the special case of cc import, where there is no content association. The resource should
1015         //still be imported.
1016         if(!isset($content_info['parent_content_id'])){
1017                 //if this is a question bank 
1018                 if ($content_info['type']=="imsqti_xmlv1p2/imscc_xmlv1p0/question-bank"){
1019                         addQuestions($content_info['href'], $content_info, $import_path);
1020                 }
1021         }
1022
1023         //if it has no title, most likely it is not a page but just a normal item, skip it
1024         if (!isset($content_info['title'])){
1025                 continue;
1026         }
1027         
1028         //check dependency immediately, then handles it
1029         $head = '';
1030         if (is_array($content_info['dependency']) && !empty($content_info['dependency'])){
1031                 foreach($content_info['dependency'] as $dependency_ref){
1032                         //handle styles 
1033                         /** handled by get_html_head in vitals.inc.php
1034                         if (preg_match('/(.*)\.css$/', $items[$dependency_ref]['href'])){
1035                                 //calculate where this is based on our current base_href. 
1036                                 //assuming the dependency folders are siblings of the item
1037                                 $head = '<link rel="stylesheet" type="text/css" href="../'.$items[$dependency_ref]['href'].'" />';
1038                         }
1039                         */
1040                         //check if this is a discussion tool dependency
1041                         if ($items[$dependency_ref]['type']=='imsdt_xmlv1p0'){
1042                                 $items[$item_id]['forum'][$dependency_ref] = $items[$dependency_ref]['href'];
1043                         }
1044                         //check if this is a QTI dependency
1045                         if (strpos($items[$dependency_ref]['type'], 'imsqti_xmlv1p2/imscc_xmlv1p0') !== false){
1046                                 $items[$item_id]['tests'][$dependency_ref] = $items[$dependency_ref]['href'];
1047                         }
1048                 }
1049         }
1050
1051         //check file array, see if there are css. 
1052         //edited nov 26, harris
1053         //removed cuz i added link to the html_tags
1054         /*
1055         if (is_array($content_info['file']) && !empty($content_info['file'])){
1056                 foreach($content_info['file'] as $dependency_ref){
1057                         //handle styles 
1058                         if (preg_match('/(.*)\.css$/', $dependency_ref)){
1059                                 //calculate where this is based on our current base_href. 
1060                                 //assuming the dependency folders are siblings of the item
1061                                 $head = '<link rel="stylesheet" type="text/css" href="'.$dependency_ref.'" />';
1062                         }
1063                 }
1064         }
1065         */
1066
1067         // remote href
1068         if (preg_match('/^http.*:\/\//', trim($content_info['href'])) )
1069         {
1070                 $content = '<a href="'.$content_info['href'].'" target="_blank">'.$content_info['title'].'</a>';
1071         }
1072         else
1073         {
1074                 if ($content_type == 'IMS Common Cartridge'){
1075                         //to handle import with purely images but nothing else
1076                         //don't need a content base path for it.
1077                         $content_new_path = $content_info['new_path'];
1078                         $content_info['new_path'] = '';
1079                 }
1080                 if (isset($content_info['href'], $xml_base_path)) {
1081                         $content_info['href'] = $xml_base_path . $content_info['href'];
1082                 }
1083                 if (!isset($content_info['href'])) {
1084                         // this item doesn't have an identifierref. so create an empty page.
1085                         // what we called a folder according to v1.2 Content Packaging spec
1086                         // Hop over
1087                         $content = '';
1088                         $ext = '';
1089                         $last_modified = date('Y-m-d H:i:s');
1090                 } else {
1091                         //$file_info = @stat(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$content_info['href']);
1092                         $file_info = @stat($import_path.$content_info['href']);
1093                         if ($file_info === false) {
1094                                 continue;
1095                         }
1096                 
1097                         //$path_parts = pathinfo(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$content_info['href']);
1098                         $path_parts = pathinfo($import_path.$content_info['href']);
1099                         $ext = strtolower($path_parts['extension']);
1100
1101                         $last_modified = date('Y-m-d H:i:s', $file_info['mtime']);
1102                 }
1103                 if (in_array($ext, array('gif', 'jpg', 'bmp', 'png', 'jpeg'))) {
1104                         /* this is an image */
1105                         $content = '<img src="'.$content_info['href'].'" alt="'.$content_info['title'].'" />';
1106                 } else if ($ext == 'swf') {
1107                         /* this is flash */
1108             /* Using default size of 550 x 400 */
1109
1110                         $content = '<object type="application/x-shockwave-flash" data="' . $content_info['href'] . '" width="550" height="400"><param name="movie" value="'. $content_info['href'] .'" /></object>';
1111
1112                 } else if ($ext == 'mov') {
1113                         /* this is a quicktime movie  */
1114             /* Using default size of 550 x 400 */
1115
1116                         $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>';
1117
1118                 /* Oct 19, 2009
1119                  * commenting this whole chunk out.  It's part of my test import codes, not sure why it's here, 
1120                  * and I don't think it should be here.  Remove this whole comment after further testing and confirmation.
1121                  * @harris
1122                  *
1123                         //Mimic the array for now.
1124                         $test_attributes['resource']['href'] = $test_xml_file;
1125                         $test_attributes['resource']['type'] = isset($items[$item_id]['type'])?'imsqti_xmlv1p2':'imsqti_xmlv1p1';
1126                         $test_attributes['resource']['file'] = $items[$item_id]['file'];
1127 //                      $test_attributes['resource']['file'] = array($test_xml_file);
1128
1129                         //Get the XML file out and start importing them into our database.
1130                         //TODO: See question_import.php 287-289.
1131                         $qids = $qti_import->importQuestions($test_attributes);
1132                 
1133                  */
1134                 } else if ($ext == 'mp3') {
1135                         $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>';
1136                 } else if (in_array($ext, array('wav', 'au'))) {
1137                         $content = '<embed SRC="'.$content_info['href'].'" autostart="false" width="145" height="60"><noembed><bgsound src="'.$content_info['href'].'"></noembed></embed>';
1138
1139                 } else if (in_array($ext, array('txt', 'css', 'html', 'htm', 'csv', 'asc', 'tsv', 'xml', 'xsl'))) {
1140                         if ($content_type == 'IMS Common Cartridge'){
1141                                 $content_info['new_path'] = $content_new_path;
1142                         }
1143
1144                         /* this is a plain text file */
1145                         //$content = file_get_contents(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$content_info['href']);
1146                         $content = file_get_contents($import_path.$content_info['href']);
1147                         if ($content === false) {
1148                                 /* if we can't stat() it then we're unlikely to be able to read it */
1149                                 /* so we'll never get here. */
1150                                 continue;
1151                         }
1152
1153                         // get the contents of the 'head' element
1154                         $head .= get_html_head_by_tag($content, $html_head_tags);
1155                         
1156                         // Specifically handle eXe package
1157                         // NOTE: THIS NEEDS WORK! TO FIND A WAY APPLY EXE .CSS FILES ONLY ON COURSE CONTENT PART.
1158                         // NOW USE OUR OWN .CSS CREATED SOLELY FOR EXE
1159                         $isExeContent = false;
1160
1161                         // check xml file in eXe package
1162                         if (preg_match("/<organization[ ]*identifier=\"eXe*>*/", $ims_manifest_xml))
1163                         {
1164                                 $isExeContent = true;
1165                         }
1166
1167                         // use ATutor's eXe style sheet as the ones from eXe conflicts with ATutor's style sheets
1168                         if ($isExeContent)
1169                         {
1170                                 $head = preg_replace ('/(<style.*>)(.*)(<\/style>)/ms', '\\1@import url(/docs/exestyles.css);\\3', $head);
1171                         }
1172
1173                         // end of specifically handle eXe package
1174
1175                         $content = get_html_body($content);
1176                         if ($contains_glossary_terms) 
1177                         {
1178                                 // replace glossary content package links to real glossary mark-up using [?] [/?]
1179                                 // refer to bug 3641, edited by Harris
1180                                 $content = preg_replace('/<a href="([.\w\d\s]+[^"]+)" target="body" class="at-term">([.\w\d\s&;"]+|.*)<\/a>/i', '[?]\\2[/?]', $content);
1181                         }
1182
1183                         /* potential security risk? */
1184                         if ( strpos($content_info['href'], '..') === false && !preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $content_info['href'])) {
1185 //                              @unlink(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$content_info['href']);
1186                         }
1187
1188                         /* overwrite content if this is discussion tool. */
1189                         if ($content_info['type']=='imsdt_xmlv1p0'){
1190                                 $dt_parser = new DiscussionToolsParser();
1191                                 $xml_content = @file_get_contents($import_path . $content_info['href']);
1192                                 $dt_parser->parse($xml_content);
1193                                 $forum_obj = $dt_parser->getDt();
1194                                 $content = $forum_obj->getText();
1195                                 unset($forum_obj);
1196                                 $dt_parser->close();
1197                         }
1198                 } else if ($ext) {
1199                         /* non text file, and can't embed (example: PDF files) */
1200                         $content = '<a href="'.$content_info['href'].'">'.$content_info['title'].'</a>';
1201                 }       
1202         }
1203         $content_parent_id = $cid;
1204         if ($content_info['parent_content_id'] !== 0) {
1205                 $content_parent_id = $items[$content_info['parent_content_id']]['real_content_id'];
1206                 //if it's not there, use $cid
1207                 if (!$content_parent_id){
1208                         $content_parent_id = $cid;
1209                 }
1210         }
1211
1212         $my_offset = 0;
1213         if ($content_parent_id == $cid) {
1214                 $my_offset = $order_offset;
1215         }
1216
1217         /* replace the old path greatest common denomiator with the new package path. */
1218         /* we don't use str_replace, b/c there's no knowing what the paths may be         */
1219         /* we only want to replace the first part of the path.  
1220         */
1221         if(is_array($all_package_base_path)){
1222                 $all_package_base_path = implode('/', $all_package_base_path);
1223         }
1224
1225     // The following condition checks if there is a common path, if so, remove it from the content base_href.
1226     // This prevents the path to grow longer.
1227         if ($common_path != '' 
1228                 && ($content_info['new_path'] === $common_path
1229                 || substr($content_info['new_path'], strlen($common_path)))) {
1230                 $content_info['new_path'] = $package_base_name . substr($content_info['new_path'], strlen($common_path));
1231         } else {
1232                 $content_info['new_path'] = $package_base_name . '/' . $content_info['new_path'];
1233         }
1234
1235         //handles weblinks
1236         if ($content_info['type']=='imswl_xmlv1p0'){
1237                 $weblinks_parser = new WeblinksParser();
1238                 $xml_content = @file_get_contents($import_path . $content_info['href']);
1239                 $weblinks_parser->parse($xml_content);
1240                 $content_info['title'] = $weblinks_parser->getTitle();
1241                 $content = $weblinks_parser->getUrl();
1242                 $content_folder_type = CONTENT_TYPE_WEBLINK;
1243                 $content_formatting = 2;
1244         }
1245         $head = addslashes($head);
1246         $content_info['title'] = addslashes($content_info['title']);
1247         $content_info['test_message'] = addslashes($content_info['test_message']);
1248
1249         //if this file is a test_xml, create a blank page instead, for imscc.
1250         if (preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $content_info['href']) 
1251                 || preg_match('/imsqti\_(.*)/', $content_info['type'])) {
1252                 $content = ' ';
1253         } else {
1254                 $content = addslashes($content);
1255         }
1256
1257         //check for content_type
1258         if ($content_formatting!=CONTENT_TYPE_WEBLINK){
1259                 $content_folder_type = (!isset($content_info['type'])?CONTENT_TYPE_FOLDER:CONTENT_TYPE_CONTENT);
1260         }
1261
1262         $sql= 'INSERT INTO '.TABLE_PREFIX.'content'
1263               . '(course_id, 
1264                   content_parent_id, 
1265                   ordering,
1266                   last_modified, 
1267                   revision, 
1268                   formatting, 
1269                   release_date,
1270                   head,
1271                   use_customized_head,
1272                   keywords, 
1273                   content_path, 
1274                   title, 
1275                   text,
1276                           test_message,
1277                           content_type) 
1278                VALUES 
1279                              ('.$_SESSION['course_id'].','                                                                                                                      
1280                              .intval($content_parent_id).','            
1281                              .($content_info['ordering'] + $my_offset - $lti_offset[$content_info['parent_content_id']] + 1).','
1282                              .'"'.$last_modified.'",                                                                                                    
1283                               0,'
1284                              .$content_formatting.' ,
1285                               NOW(),"'
1286                              . $head .'",
1287                              1,
1288                               "",'
1289                              .'"'.$content_info['new_path'].'",'
1290                              .'"'.$content_info['title'].'",'
1291                              .'"'.$content.'",'
1292                                  .'"'.$content_info['test_message'].'",'
1293                                  .$content_folder_type.')';
1294
1295         $result = mysql_query($sql, $db) or die(mysql_error());
1296
1297         /* get the content id and update $items */
1298         $items[$item_id]['real_content_id'] = mysql_insert_id($db);
1299
1300         /* get the tests associated with this content */
1301         if (!empty($items[$item_id]['tests']) || strpos($items[$item_id]['type'], 'imsqti_xmlv1p2/imscc_xmlv1p0') !== false){
1302                 $qti_import = new QTIImport($import_path);
1303                 if (isset($items[$item_id]['tests'])){
1304                         $loop_var = $items[$item_id]['tests'];
1305                 } else {
1306                         $loop_var = $items[$item_id]['file'];
1307                 }
1308
1309                 foreach ($loop_var as $array_id => $test_xml_file){
1310                         //check if this item is the qti item object, or it is the content item obj
1311                         //switch it to qti obj if it's content item obj
1312                         if ($items[$item_id]['type'] == 'webcontent'){
1313                                 $item_qti = $items[$array_id];
1314                         } else {
1315                                 $item_qti = $items[$item_id];
1316                         }
1317                         //call subrountine to add the questions.
1318                         $qids = addQuestions($test_xml_file, $item_qti, $import_path);
1319
1320                         //import test
1321                         if ($test_title==''){
1322                                 $test_title = $content_info['title'];
1323                         }
1324
1325                         $tid = $qti_import->importTest($test_title);
1326
1327                         //associate question and tests
1328                         foreach ($qids as $order=>$qid){
1329                                 if (isset($qti_import->weights[$order])){
1330                                         $weight = round($qti_import->weights[$order]);
1331                                 } else {
1332                                         $weight = 0;
1333                                 }
1334                                 $new_order = $order + 1;
1335                                 $sql = "INSERT INTO " . TABLE_PREFIX . "tests_questions_assoc" . 
1336                                                 "(test_id, question_id, weight, ordering, required) " .
1337                                                 "VALUES ($tid, $qid, $weight, $new_order, 0)";
1338                                 $result = mysql_query($sql, $db);
1339                         }
1340
1341                         //associate content and test
1342                         $sql =  'INSERT INTO ' . TABLE_PREFIX . 'content_tests_assoc' . 
1343                                         '(content_id, test_id) ' .
1344                                         'VALUES (' . $items[$item_id]['real_content_id'] . ", $tid)";
1345                         $result = mysql_query($sql, $db);
1346                 
1347 //                      if (!$msg->containsErrors()) {
1348 //                              $msg->addFeedback('IMPORT_SUCCEEDED');
1349 //                      }
1350                 }
1351         }
1352
1353         /* get the a4a related xml */
1354         if (isset($items[$item_id]['a4a_import_enabled']) && isset($items[$item_id]['a4a']) && !empty($items[$item_id]['a4a'])) {
1355                 $a4a_import = new A4aImport($items[$item_id]['real_content_id']);
1356                 $a4a_import->setRelativePath($items[$item_id]['new_path']);
1357                 $a4a_import->importA4a($items[$item_id]['a4a']);
1358         }
1359
1360         /* get the discussion tools (dependent to content)*/
1361         if (isset($items[$item_id]['forum']) && !empty($items[$item_id]['forum'])){
1362                 foreach($items[$item_id]['forum'] as $forum_ref => $forum_link){
1363                         $dt_parser = new DiscussionToolsParser();
1364                         $dt_import = new DiscussionToolsImport();
1365
1366                         //if this forum has not been added, parse it and add it.
1367                         if (!isset($added_dt[$forum_ref])){
1368                                 $xml_content = @file_get_contents($import_path . $forum_link);
1369                                 $dt_parser->parse($xml_content);
1370                                 $forum_obj = $dt_parser->getDt();
1371                                 $dt_import->import($forum_obj, $items[$item_id]['real_content_id']);
1372                                 $added_dt[$forum_ref] = $dt_import->getFid();                           
1373                         }
1374                         //associate the fid and content id
1375                         $dt_import->associateForum($items[$item_id]['real_content_id'], $added_dt[$forum_ref]);
1376                 }
1377         } elseif ($items[$item_id]['type']=='imsdt_xmlv1p0'){
1378                 //optimize this, repeated codes as above
1379                 $dt_parser = new DiscussionToolsParser();
1380                 $dt_import = new DiscussionToolsImport();
1381                 $xml_content = @file_get_contents($import_path . $content_info['href']);
1382                 $dt_parser->parse($xml_content);
1383                 $forum_obj = $dt_parser->getDt();
1384                 $dt_import->import($forum_obj, $items[$item_id]['real_content_id']);
1385                 $added_dt[$item_id] = $dt_import->getFid();
1386
1387                 //associate the fid and content id
1388                 $dt_import->associateForum($items[$item_id]['real_content_id'], $added_dt[$item_id]);
1389         }
1390 }
1391
1392 if ($package_base_path == '.') {
1393         $package_base_path = '';
1394 }
1395
1396 // loop through the files outside the package folder, and copy them to its relative path
1397 /**
1398 if (is_dir(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/resources')) {
1399         $handler = opendir(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/resources');
1400         while ($file = readdir($handler)){
1401                 $filename = AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/resources/'.$file;
1402 debug($filename);
1403                 if(is_file($filename)){
1404                         @rename($filename, AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$package_base_name.'/'.$file);
1405                 }
1406         }
1407         closedir($handler);
1408 }
1409 **/
1410 //--- harris edit for path thing
1411 $file = AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].DIRECTORY_SEPARATOR.$common_path;
1412 if (is_dir($file)) {
1413     rename($file, AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$package_base_name);
1414 }
1415 //--- end
1416 //takes care of the condition where the whole package doesn't have any contents but question banks
1417 //also is the case of urls
1418 if(is_array($all_package_base_path)){
1419         $all_package_base_path = implode('/', $all_package_base_path);
1420         if(strpos($all_package_base_path, 'http:/')===false){
1421         if (@rename($import_path.$all_package_base_path, AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$package_base_name) === false) {
1422             if (!$msg->containsErrors()) {
1423                 $msg->addError('IMPORT_FAILED');
1424             }
1425         }
1426     }
1427 }
1428 //exit;//harris
1429 //check if there are still resources missing
1430 /*
1431 foreach($items as $idetails){
1432         $temp_path = pathinfo($idetails['href']);
1433         @rename(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$temp_path['dirname'], AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$package_base_name . '/' . $temp_path['dirname']);
1434 }
1435 */
1436 clr_dir(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id']);
1437
1438 if (file_exists($full_filename)) {
1439         @unlink($full_filename);
1440 }
1441
1442
1443 if ($_POST['s_cid']){
1444         if (!$msg->containsErrors()) {
1445                 $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY');
1446         }
1447         header('Location: ../editor/edit_content.php?cid='.intval($_POST['cid']));
1448         exit;
1449 } else {
1450         if (!$msg->containsErrors()) {
1451                 $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY');
1452         }
1453         if ($_GET['tile']) {
1454                 header('Location: '.AT_BASE_HREF.'mods/_standard/tile_search/index.php');
1455         } else {
1456                 header('Location: ./index.php?cid='.intval($_POST['cid']));
1457         }
1458         exit;
1459 }
1460
1461 ?>