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