2 /************************************************************************/
4 /************************************************************************/
5 /* Copyright (c) 2010 */
6 /* Inclusive Design Institute */
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 /************************************************************************/
13 /** Commented by Cindy Li on Feb 2, 2010
14 * Modified from ATutor mods/_core/imscp/ims_import.php, SVN revision 9126
17 define('TR_INCLUDE_PATH', '../../include/');
19 // Validate OAuth token and set $SESSION['user_id']
20 // Must come before require(vitals.inc.php) because vitals redirects to index page
21 // when $SESSION['user_id'] is not set
22 $oauth_import = false; // whether the import request is from oauth web service
24 // By default, enable the import of associated tests and a4a objects
26 if (!isset($_POST['allow_test_import'])) $_POST['allow_test_import'] = 1;
27 if (!isset($_POST['allow_a4a_import'])) $_POST['allow_a4a_import'] = 1;
29 // the import request is from oauth web service, find the user id from the given token
30 if (isset($_GET['oauth_token']))
32 require_once(TR_INCLUDE_PATH.'config.inc.php');
33 require_once(TR_INCLUDE_PATH.'constants.inc.php');
35 if ($_GET['oauth_token'] == '')
37 echo "error=".urlencode('Empty OAuth token');
43 require_once(TR_INCLUDE_PATH.'classes/DAO/OAuthServerTokensDAO.class.php');
44 $oAuthServerTokensDAO = new OAuthServerTokensDAO();
45 $token_row = $oAuthServerTokensDAO->getByTokenAndType($_GET['oauth_token'], 'access');
47 if (!is_array($token_row))
49 echo "error=".urlencode('Invalid OAuth token');
52 else if ($oAuthServerTokensDAO->isTokenExpired($_GET['oauth_token']))
54 echo "error=".urlencode('OAuth token expired');
58 $_user_id = $token_row[0]['user_id'];
62 require(TR_INCLUDE_PATH.'vitals.inc.php');
64 require_once(TR_INCLUDE_PATH.'classes/Utility.class.php');
65 require_once(TR_INCLUDE_PATH.'../home/classes/ContentUtility.class.php');
66 require_once(TR_INCLUDE_PATH.'classes/DAO/UsersDAO.class.php');
67 require_once(TR_INCLUDE_PATH.'classes/DAO/CoursesDAO.class.php');
68 require_once(TR_INCLUDE_PATH.'classes/DAO/UserCoursesDAO.class.php');
69 require_once(TR_INCLUDE_PATH.'classes/DAO/ContentDAO.class.php');
70 require_once(TR_INCLUDE_PATH.'classes/DAO/TestsQuestionsAssocDAO.class.php');
71 require_once(TR_INCLUDE_PATH.'classes/DAO/ContentTestsAssocDAO.class.php');
72 require_once(TR_INCLUDE_PATH.'classes/FileUtility.class.php'); /* for clr_dir() and preImportCallBack and dirsize() */
74 require_once(TR_INCLUDE_PATH.'lib/pclzip.lib.php');
75 require_once(TR_INCLUDE_PATH.'lib/pclzip_callback.lib.php');
76 require_once(TR_INCLUDE_PATH.'lib/qti.inc.php');
77 //require(TR_INCLUDE_PATH.'classes/QTI/QTIParser.class.php');
78 require_once(TR_INCLUDE_PATH.'classes/QTI/QTIImport.class.php');
79 require_once(TR_INCLUDE_PATH.'classes/A4a/A4aImport.class.php');
80 require(TR_INCLUDE_PATH.'../home/ims/ns.inc.php'); //namespace, no longer needs, delete it after it's stable.
81 require_once(TR_INCLUDE_PATH.'classes/Weblinks/WeblinksParser.class.php');
82 require(TR_INCLUDE_PATH.'classes/DiscussionTools/DiscussionToolsParser.class.php');
83 require(TR_INCLUDE_PATH.'classes/DiscussionTools/DiscussionToolsImport.class.php');
85 // make sure the user has author privilege
86 Utility::authenticate(TR_PRIV_ISAUTHOR);
88 /* to avoid timing out on large files */
90 $_SESSION['done'] = 1;
92 $html_head_tags = array("style", "script", "link");
94 $package_base_path = '';
95 $package_real_base_path = ''; //the path to save the contents
96 $all_package_base_path = array();
98 $element_path = array();
99 $imported_glossary = array();
100 $character_data = '';
104 $skip_ims_validation = false;
105 $added_dt = array(); //the mapping of discussion tools that are added
106 $avail_dt = array(); //list of discussion tools that have not been handled
108 function check_available_size($course_id)
110 global $coursesDAO, $MaxCourseSize, $import_path, $msg, $oauth_import;
112 $q_row = $coursesDAO->get($course_id);
114 //$sql = "SELECT max_quota FROM ".TABLE_PREFIX."courses WHERE course_id=$_SESSION[course_id]";
115 //$result = mysql_query($sql, $db);
116 //$q_row = mysql_fetch_assoc($result);
118 if ($q_row['max_quota'] == TR_COURSESIZE_UNLIMITED) return;
119 else $zip_size_limit = $MaxCourseSize;
121 $totalBytes = FileUtility::dirsize($import_path);
123 $total_after = $zip_size_limit - $totalBytes;
125 if (is_dir(TR_CONTENT_DIR . $course_id.'/'))
127 $course_total = FileUtility::dirsize(TR_CONTENT_DIR . $course_id.'/');
128 $total_after -= $course_total;
131 if ($total_after < 0) {
132 /* remove the content dir, since there's no space for it */
133 $errors = array('NO_CONTENT_SPACE', number_format(-1*($total_after/TR_KBYTE_SIZE), 2 ) );
134 $msg->addError($errors);
136 // Clean up import path and inserted course row
137 FileUtility::clr_dir($import_path);
138 $coursesDAO->Delete($course_id);
140 if (isset($_GET['tile'])) {
141 header('Location: '.$_base_path.'tools/tile/index.php');
143 else if ($oauth_import) {
144 echo "error=".urlencode('No space for the content.');
147 header('Location: '.$_SERVER['HTTP_REFERER']);
154 * return the error messages represented by the given array
156 * @ref http://ca3.php.net/manual/en/domdocument.schemavalidate.php
158 function libxml_display_error($error)
161 switch ($error->level) {
162 case LIBXML_ERR_WARNING:
163 $return .= "<b>Warning $error->code</b>: ";
165 case LIBXML_ERR_ERROR:
166 $return .= "<b>Error $error->code</b>: ";
168 case LIBXML_ERR_FATAL:
169 $return .= "<b>Fatal Error $error->code</b>: ";
172 $return .= trim($error->message);
174 $return .= " in <b>$error->file</b>";
176 $return .= " on line <b>$error->line</b>\n";
182 * Validate all the XML in the package, including checking XSDs, missing data.
183 * @param string the path of the directory that contains all the package files
184 * @return boolean true if every file exists in the manifest, false if any is missing.
186 function checkResources($import_path){
187 global $items, $msg, $skip_ims_validation, $avail_dt;
189 if (!is_dir($import_path)){
193 //if the package has access for all content, skip validation for now.
194 //todo: import the XSD into our validator
195 if ($skip_ims_validation){
199 //generate a file tree
200 $data = rscandir($import_path);
202 //check if every file is presented in the manifest
203 foreach($data as $filepath){
204 $filepath = substr($filepath, strlen($import_path));
206 //validate xml via its xsd/dtds
207 if (preg_match('/(.*)\.xml/', $filepath)){
208 libxml_use_internal_errors(true);
209 $dom = new DOMDocument();
210 $dom->load(realpath($import_path.$filepath));
211 if (!@$dom->schemaValidate('main.xsd')){
212 $errors = libxml_get_errors();
213 foreach ($errors as $error) {
215 if ($error->level==LIBXML_ERR_WARNING){
218 $msg->addError(array('IMPORT_CARTRIDGE_FAILED', libxml_display_error($error)));
220 libxml_clear_errors();
225 //Create an array that mimics the structure of the data array, based on the xml items
226 $filearray = array();
227 foreach($items as $name=>$fileinfo){
228 if(isset($fileinfo['file']) && is_array($fileinfo['file']) && !empty($fileinfo['file'])){
229 foreach($fileinfo['file'] as $fn){
230 if (!in_array(realpath($import_path.$fn), $filearray)){
232 if (preg_match('/^http[s]?\:/', $fn) == 0){
233 $filearray[] = realpath($import_path. $fn);
239 //validate the xml by its schema
240 if (preg_match('/imsqti\_(.*)/', $fileinfo['type'])){
241 $qti = new QTIParser($fileinfo['type']);
242 $xml_content = @file_get_contents($import_path . $fileinfo['href']);
243 $qti->parse($xml_content); //will add error to $msg if failed
246 //add all dependent discussion tools to a list
247 if(isset($fileinfo['dependency']) && !empty($fileinfo['dependency'])){
248 $avail_dt = array_merge($avail_dt, $fileinfo['dependency']);
252 //check if all files in the xml is presented in the archieve
253 $result = array_diff($filearray, $data);
254 //using sizeof because array_diff only
255 //returns an array containing all the entries from array1 that are not present in any of the
257 //Using sizeof make sure it's not a subset of array2.
258 //-1 on data because it always contain the imsmanifest.xml file
259 if (!$skip_ims_validation){
260 if (!empty($result) || sizeof($data)-1>sizeof($filearray)){
261 $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('ims_missing_references')));
268 * @example rscandir(dirname(__FILE__).'/'));
269 * @param string $base
274 function rscandir($base='', &$data=array()) {
275 $array = array_diff(scandir($base), array('.', '..')); # remove ' and .. from the array */
276 foreach($array as $value) : /* loop through the array at the level of the supplied $base */
278 if (is_dir($base.$value)) : /* if this is a directory */
279 // don't save the directory name
280 $data = rscandir($base.$value.'/', $data); /* then make a recursive call with the
281 current $value as the $base supplying the $data array to carry into the recursion */
283 elseif (is_file($base.$value)) : /* else if the current $value is a file */
284 $data[] = realpath($base.$value); /* just add the current $value to the $data array */
289 return $data; // return the $data array
294 * Function to restructure the $items. So that old import will merge the top page into its children, and
295 * create a new folder on top of it
297 function rehash($items){
299 $parent_page_maps = array(); //old=>new
300 $temp_popped_items = array();
301 $rehashed_items = array(); //the reconstructed array
302 foreach($items as $id => $content){
303 $parent_obj = $items[$content['parent_content_id']];
304 $rehashed_items[$id] = $content; //copy
305 if (isset($parent_page_maps[$content['parent_content_id']])){
306 $rehashed_items [$id]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']];
307 $rehashed_items [$id]['ordering']++;
309 //If its parent page is a top page and have an identiferref
310 elseif (isset($parent_obj) && isset($parent_obj['href'])){
311 if (!isset($parent_obj['href'])){
312 //check if this top page is already a folder, if so, next.
315 //else, make its parent page to a folder
316 $new_item['title'] = $parent_obj['title'];
317 //check if this parent has been modified, if so, chnage it
318 if (isset($parent_page_maps[$parent_obj['parent_content_id']])){
319 $new_item['parent_content_id'] = $parent_page_maps[$parent_obj['parent_content_id']];
321 $new_item['parent_content_id'] = $parent_obj['parent_content_id'];
323 //all ordering needs to be +1 because we are creating a new folder on top of
324 //everything, except the first page.
325 $new_item['ordering'] = $parent_obj['ordering'];
326 if ($new_item['parent_content_id']!='0'){
327 $new_item['ordering']++;
330 //assign this new parent folder to the pending items array
331 $new_item_name = $content['parent_content_id'].'_FOLDER';
332 //a not so brilliant way to append the folder in its appropriate position
333 $reordered_hashed_items = array(); //use to store the new rehashed item with the correct item order
334 foreach($rehashed_items as $rh_id=>$rh_content){
335 if ($rh_id == $content['parent_content_id']){
336 //add the folder in before the parent subpage.
337 $reordered_hashed_items[$new_item_name] = $new_item;
339 $reordered_hashed_items[$rh_id] = $rh_content; //clone
341 $rehashed_items = $reordered_hashed_items; //replace it back
342 unset($reordered_hashed_items);
343 $parent_page_maps[$content['parent_content_id']] = $new_item_name; //save this page on the hash map
345 //reconstruct the parent
346 $rehashed_items[$content['parent_content_id']]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']];
347 $rehashed_items[$content['parent_content_id']]['ordering'] = 0; //always the first one.
350 $rehashed_items[$id]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']];
351 $rehashed_items[$id]['ordering']++;
355 return $rehashed_items;
359 * Take out the common path within all $items['new_path'].
360 * This allows import/export repeatedly without duplicating its path
361 * @param array contains the breakdown of all resources in the XML
363 function removeCommonPath($items){
365 $quit = false; //a flag that is set if it's not the first time being run.
367 $filearray = array();
368 //get all files listed in the manifest
369 foreach($items as $name=>$fileinfo){
370 if(isset($fileinfo['file']) && is_array($fileinfo['file']) && !empty($fileinfo['file'])){
371 foreach($fileinfo['file'] as $fn){
372 if (!in_array($fn, $filearray)){
373 if (preg_match('/^http[s]?\:/', $fn) == 0){
381 foreach($filearray as $index=>$path){
383 //check if this is a XML file; if so, skip through,
384 //cause XML most likely isn't a content resource.
385 $ext = substr($path, (strrpos($path, '.')+1));
390 //if common path is empty, assign the first path to it.
391 if ($common_path=='' && $quit==false){
392 $common_path = $path;
393 $quit = true; //the next time common_path is empty, quit;
396 //we use '/' here instead of DIRECTORY_SEPARATOR because php would
397 //actually use '\' and return the whole string.
398 $common_array = explode('/', $common_path);
399 $path_array = explode('/', $path);
400 //convert path_array to absolute path
401 //TODO: array_search is slow, build a faster search
402 $pos=array_search('..', $path_array);
403 while($pos=array_search('..', $path_array)){
404 array_splice($path_array, $pos-1, 2);
406 $intersect_array = array_intersect($common_array, $path_array);
407 $common_path = implode('/', $intersect_array);
410 // If this path (including file extension) is in the filearray,
411 // then this means there is only 1 file in the array, implies there
412 // wasn't any intersection at all. In this case, use the base root.
413 if(in_array($common_path, $filearray)) {
422 * This function will take the test accessment XML and add these to the database.
423 * @param string The path of the XML, without the import_path.
424 * @param mixed An item singleton. Contains the info of this item, namely, the accessment details.
425 * The item must be an object created by the ims class.
426 * @param string the import path
427 * @return mixed An Array that contains all the question IDs that have been imported.
429 function addQuestions($xml, $item, $import_path){
431 $qti_import = new QTIImport($import_path);
432 $tests_xml = $import_path.$xml;
434 //Mimic the array for now.
435 $test_attributes['resource']['href'] = $item['href'];
436 $test_attributes['resource']['type'] = preg_match('/imsqti_xmlv1p2/', $item['type'])==1?'imsqti_xmlv1p2':'imsqti_xmlv1p1';
437 $test_attributes['resource']['file'] = $item['file'];
439 //Get the XML file out and start importing them into our database.
440 //TODO: See question_import.php 287-289.
441 $qids = $qti_import->importQuestions($test_attributes);
442 $test_title = $qti_import->title;
448 /* called at the start of en element */
449 /* builds the $path array which is the path from the root to the current element */
450 function startElement($parser, $name, $attrs) {
451 global $items, $path, $package_base_path, $all_package_base_path, $package_real_base_path;
452 global $element_path, $import_path, $skip_ims_validation;
453 global $xml_base_path, $test_message, $content_type;
454 global $current_identifier, $msg, $ns, $ns_cp;
455 global $course_primary_lang;
457 // get language from CONTENT PACKAGE
458 if (substr($element_path[count($element_path)-1], -6) == ':title' && substr($name, -11) == ':langstring') {
459 $course_primary_lang = trim($attrs['xml:lang']);
462 //validate namespaces
463 if(!$skip_ims_validation && isset($attrs['xsi:schemaLocation']) && $name=='manifest'){
464 $schema_location = array();
465 $split_location = preg_split('/[\r\n\s]+/', trim($attrs['xsi:schemaLocation']));
467 //check if the namespace is actually right, have an array or some sort in IMS class
468 if(sizeof($split_location)%2==1){
469 //schema is not in the form of "The first URI reference in each pair is a namespace name,
470 //and the second is the location of a schema that describes that namespace."
471 //$msg->addError('MANIFEST_NOT_WELLFORM');
472 $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('schema_error')));
475 //turn the xsi:schemaLocation URI into a schema that describe namespace.
477 //http://msdn.microsoft.com/en-us/library/ms256100(VS.85).aspx
478 //http://www.w3.org/TR/xmlschema-1/
479 for($i=0; $i < sizeof($split_location);$i=$i+2){
480 //if the key of the namespace is not defined. Throw error.
481 if(!isset($ns[$split_location[$i]]) && !isset($ns_cp[$split_location[$i]])){
482 $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('schema_error')));
489 if ($name == 'manifest' && isset($attrs['xml:base']) && $attrs['xml:base']) {
490 $xml_base_path = $attrs['xml:base'];
491 } else if ($name == 'file') {
492 // check if it misses file references
493 if(!$skip_ims_validation && (!isset($attrs['href']) || $attrs['href']=='')){
494 //$msg->addError('MANIFEST_NOT_WELLFORM');
495 $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('ims_missing_references')));
498 // special case for webCT content packages that don't specify the `href` attribute
499 // with the `<resource>` element.
500 // we take the `href` from the first `<file>` element.
501 if (isset($items[$current_identifier]) && ($items[$current_identifier]['href'] == '')) {
502 $attrs['href'] = urldecode($attrs['href']);
503 $items[$current_identifier]['href'] = $attrs['href'];
505 if ($temp_path['extension'] == 'html') {
506 //$temp_path = pathinfo($attrs['href']);
507 $temp_path = explode('/', $temp_path['dirname']);
508 if (empty($package_base_path)){
509 $package_base_path = $temp_path;
511 if ($all_package_base_path!='' && empty($all_package_base_path)){
512 $all_package_base_path = $temp_path;
514 $package_base_path = array_intersect_assoc($package_base_path, $temp_path);
516 //calculate the depths of relative paths
517 if ($all_package_base_path!=''){
518 $no_relative_temp_path = $temp_path;
519 foreach($no_relative_temp_path as $path_node){
520 if ($path_node=='..'){
521 array_pop($no_relative_temp_path);
522 array_pop($no_relative_temp_path); //not a typo, have to pop twice, both itself('..'), and the one before.
525 $all_package_base_path = array_intersect_assoc($all_package_base_path, $no_relative_temp_path);
526 if (empty($all_package_base_path)){
527 $all_package_base_path = ''; //unset it, there is no intersection.
531 //save the actual content base path
532 if (in_array('..', $temp_path)){
533 $sizeofrp = array_count_values($temp_path);
536 //for IMSCC, assume that all resources lies in the same folder, except styles.css
537 if ($items[$current_identifier]['type']=='webcontent' || $items[$current_identifier]['type']=='imsdt_xmlv1p0'){
538 //find the intersection of each item's related files, then that intersection is the content_path
539 if (isset($items[$current_identifier]['file'])){
540 foreach ($items[$current_identifier]['file'] as $resource_path){
541 $temp_path = pathinfo($resource_path);
542 $temp_path = explode('/', $temp_path['dirname']);
543 $package_base_path = array_intersect_assoc($package_base_path, $temp_path);
549 if($sizeofrp['..'] > 0 && !empty($all_package_base_path)){
550 for ($i=0; $i<$sizeofrp['..']; $i++){
551 array_pop($all_package_base_path);
554 //if (count($package_base_path) > 0) {
555 if (!empty($package_base_path)) {
556 $items[$current_identifier]['new_path'] = implode('/', $package_base_path);
559 if (isset($_POST['allow_test_import']) && isset($items[$current_identifier])
560 && preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $attrs['href'])) {
561 $items[$current_identifier]['tests'][] = $attrs['href'];
563 if (isset($_POST['allow_a4a_import']) && isset($items[$current_identifier])) {
564 $items[$current_identifier]['a4a_import_enabled'] = true;
566 } else if (($name == 'item') && ($attrs['identifierref'] != '')) {
567 $path[] = $attrs['identifierref'];
568 } else if (($name == 'item') && ($attrs['identifier'])) {
569 $path[] = $attrs['identifier'];
571 } else if (($name == 'resource')) {
572 $current_identifier = $attrs['identifier'];
573 $items[$current_identifier]['type'] = $attrs['type'];
574 if ($attrs['href']) {
575 $attrs['href'] = urldecode($attrs['href']);
577 $items[$attrs['identifier']]['href'] = $attrs['href'];
579 // href points to a remote url
580 if (preg_match('/^http.*:\/\//', trim($attrs['href'])))
581 $items[$attrs['identifier']]['new_path'] = '';
582 else // href points to local file
584 $temp_path = pathinfo($attrs['href']);
585 $temp_path = explode('/', $temp_path['dirname']);
586 $package_base_path = $temp_path;
588 $items[$attrs['identifier']]['new_path'] = implode('/', $temp_path);
593 } else if ($name=='dependency' && $attrs['identifierref']!='') {
594 //if there is a dependency, attach it to the item array['file']
595 $items[$current_identifier]['dependency'][] = $attrs['identifierref'];
597 if (($name == 'item') && ($attrs['parameters'] != '')) {
598 $items[$attrs['identifierref']]['test_message'] = $attrs['parameters'];
601 if(!isset($items[$current_identifier]) && $attrs['href']!=''){
602 $items[$current_identifier]['href'] = $attrs['href'];
604 if (substr($attrs['href'], 0, 7) == 'http://' || substr($attrs['href'], 0, 8) == 'https://' || file_exists($import_path.$attrs['href']) || $skip_ims_validation){
605 $items[$current_identifier]['file'][] = $attrs['href'];
607 //$msg->addError('');
608 $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT(array('ims_files_missing', $attrs['href']))));
611 if ($name=='cc:authorizations'){
612 //don't have authorization setup.
613 //$msg->addError('');
614 $msg->addError('IMS_AUTHORIZATION_NOT_SUPPORT');
616 array_push($element_path, $name);
619 /* called when an element ends */
620 /* removed the current element from the $path */
621 function endElement($parser, $name) {
622 global $path, $element_path, $my_data, $items, $oauth_import;
623 global $current_identifier, $skip_ims_validation;
624 global $msg, $content_type;
625 global $course_title, $course_description, $course_primary_lang; // added by Cindy Li
626 static $resource_num = 0;
628 if ($name == 'item') {
632 // added by Cindy Li on Jan 10, 2010
633 // Extract course title, description and primary language for a newly-created course
634 if (substr($element_path[count($element_path)-2], -6) == ':title') {
635 if (substr($element_path[count($element_path)-1], -7) == ':string' ||
636 substr($element_path[count($element_path)-1], -11) == ':langstring') {
637 $course_title = trim($my_data);
641 if (substr($element_path[count($element_path)-2], -12) == ':description') {
642 if (substr($element_path[count($element_path)-1], -7) == ':string' ||
643 substr($element_path[count($element_path)-1], -11) == ':langstring') {
644 $course_description = trim($my_data);
648 // get language from COMMON CARTRIDGE
650 if (substr($element_path[count($element_path)-1], -9) == ':language') {
651 $course_primary_lang = trim($my_data);
654 // end of added by Cindy Li on Jan 10, 2010
656 //check if this is a test import
657 if ($name == 'schema'){
658 if (trim($my_data)=='IMS Question and Test Interoperability'){
660 echo "error=".urlencode('A test import');
662 $msg->addError('IMPORT_FAILED');
665 $content_type = trim($my_data);
669 if ($current_identifier != ''){
670 $my_data = trim($my_data);
671 $last_file_name = $items[$current_identifier]['file'][(sizeof($items[$current_identifier]['file']))-1];
673 if ($name=='originalAccessMode'){
674 if (in_array('accessModeStatement', $element_path)){
675 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['access_stmt_originalAccessMode'][] = $my_data;
676 } elseif (in_array('adaptationStatement', $element_path)){
677 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['adapt_stmt_originalAccessMode'][] = $my_data;
679 } elseif (($name=='language') && in_array('accessModeStatement', $element_path)){
680 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['language'][] = $my_data;
681 } elseif ($name=='hasAdaptation') {
682 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['hasAdaptation'][] = $my_data;
683 } elseif ($name=='isAdaptationOf'){
684 $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['isAdaptationOf'][] = $my_data;
685 } elseif ($name=='accessForAllResource'){
686 /* the head node of accessForAll Metadata, if this exists in the manifest. Skip XSD validation,
687 * because A4a doesn't have a xsd yet. Our access for all is based on ISO which will not pass
688 * the current IMS validation.
689 * Also, since ATutor is the only one (as of Oct 21, 2009) that exports IMS with access for all
690 * content, we can almost assume that any ims access for all content is by us, and is valid.
692 $skip_ims_validation = true;
694 } elseif($name=='file'){
695 $resource_num = 0; //reset resournce number to 0 when the file tags ends
699 if ($element_path === array('manifest', 'metadata', 'imsmd:lom', 'imsmd:general', 'imsmd:title', 'imsmd:langstring')) {
700 global $package_base_name;
701 $package_base_name = trim($my_data);
704 array_pop($element_path);
708 /* called when there is character data within elements */
709 /* constructs the $items array using the last entry in $path as the parent element */
710 function characterData($parser, $data){
711 global $path, $items, $order, $my_data, $element_path;
712 global $current_identifier;
714 $str_trimmed_data = trim($data);
716 if (!empty($str_trimmed_data)) {
717 $size = count($path);
719 $current_item_id = $path[$size-1];
721 $parent_item_id = $path[$size-2];
726 if (isset($items[$current_item_id]['parent_content_id']) && is_array($items[$current_item_id])) {
728 /* this item already exists, append the title */
729 /* this fixes {\n, \t, `, &} characters in elements */
731 /* horible kludge to fix the <ns2:objectiveDesc xmlns:ns2="http://www.utoronto.ca/atrc/tile/xsd/tile_objective"> */
733 if (in_array('accessForAllResource', $element_path)){
735 } elseif ($element_path[count($element_path)-1] != 'ns1:objectiveDesc') {
736 $items[$current_item_id]['title'] .= $data;
740 $order[$parent_item_id] ++;
741 $item_tmpl = array( 'title' => $data,
742 'parent_content_id' => $parent_item_id,
743 'ordering' => $order[$parent_item_id]-1);
744 //append other array values if it exists
745 if (is_array($items[$current_item_id])){
746 $items[$current_item_id] = array_merge($items[$current_item_id], $item_tmpl);
748 $items[$current_item_id] = $item_tmpl;
757 /* glossary parser: */
758 function glossaryStartElement($parser, $name, $attrs) {
759 global $element_path;
761 array_push($element_path, $name);
764 /* called when an element ends */
765 /* removed the current element from the $path */
766 function glossaryEndElement($parser, $name) {
767 global $element_path, $my_data, $imported_glossary;
768 static $current_term;
770 if ($element_path === array('glossary', 'item', 'term') ||
771 $element_path === array('glossary:glossary', 'item', 'term')) {
772 $current_term = $my_data;
774 } else if ($element_path === array('glossary', 'item', 'definition') ||
775 $element_path === array('glossary:glossary', 'item', 'definition')) {
776 $imported_glossary[trim($current_term)] = trim($my_data);
779 array_pop($element_path);
783 function glossaryCharacterData($parser, $data){
789 if (!isset($_POST['submit']) && !isset($_POST['cancel']) && !isset($_GET['oauth_token'])) {
790 /* just a catch all */
791 $msg->addError('NO_PRIV');
792 header('Location: '.$_SERVER['HTTP_REFERER']);
794 } else if (isset($_POST['cancel'])) {
795 $msg->addFeedback('IMPORT_CANCELLED');
797 header('Location: '.$_SERVER['HTTP_REFERER']);
801 $cid = intval($_POST['cid']);
803 //If user chooses to ignore validation.
804 if(isset($_POST['ignore_validation']) && $_POST['ignore_validation']==1) {
805 $skip_ims_validation = true;
808 if (isset($_REQUEST['url']) && ($_REQUEST['url'] != 'http://') ) {
809 if ($content = @file_get_contents($_REQUEST['url'])) {
810 $filename = substr(time(), -6). '.zip';
811 $full_filename = TR_CONTENT_DIR . $filename;
813 if (!$fp = fopen($full_filename, 'w+b')) {
814 echo "Cannot open file ($filename)";
818 if (fwrite($fp, $content, strlen($content) ) === FALSE) {
819 echo "Cannot write to file ($filename)";
824 $_FILES['file']['name'] = $filename;
825 $_FILES['file']['tmp_name'] = $full_filename;
826 $_FILES['file']['size'] = strlen($content);
828 // $url_parts = pathinfo($_REQUEST['url']);
829 // $package_base_name_url = $url_parts['basename'];
830 $package_base_name_url = md5(time());
832 $ext = pathinfo($_FILES['file']['name']);
833 $ext = $ext['extension'];
836 $msg->addError('IMPORTDIR_IMS_NOTVALID');
837 } else if ($_FILES['file']['error'] == 1) {
838 $errors = array('FILE_MAX_SIZE', ini_get('upload_max_filesize'));
839 $msg->addError($errors);
840 } else if ( !$_FILES['file']['name'] || (!is_uploaded_file($_FILES['file']['tmp_name']) && !$_REQUEST['url'])) {
841 $msg->addError('FILE_NOT_SELECTED');
842 } else if ($_FILES['file']['size'] == 0) {
843 $msg->addError('IMPORTFILE_EMPTY');
846 if ($msg->containsErrors()) {
847 if (isset($_GET['tile'])) {
848 header('Location: '.$_base_path.'tools/tile/index.php');
849 } else if ($oauth_import) {
850 echo "error=".urlencode('Invalid imported file');
852 header('Location: '.$_SERVER['HTTP_REFERER']);
854 if (file_exists($full_filename)) @unlink($full_filename);
858 /* check if ../content/import/ exists */
859 $import_path = TR_CONTENT_DIR . 'import/';
860 $content_path = TR_CONTENT_DIR;
862 if (!is_dir($import_path)) {
863 if (!@mkdir($import_path, 0700)) {
864 $msg->addError('IMPORTDIR_FAILED');
868 if (isset($_POST['_course_id'])) $import_path .= $_POST['_course_id'].'/';
869 else $import_path .= Utility::getRandomStr(16).'/';
871 if (is_dir($import_path)) {
872 FileUtility::clr_dir($import_path);
875 if (!@mkdir($import_path, 0700)) {
876 $msg->addError('IMPORTDIR_FAILED');
879 if ($msg->containsErrors()) {
880 if (isset($_GET['tile'])) {
881 header('Location: '.$_base_path.'tools/tile/index.php');
882 } else if ($oauth_import) {
883 echo "error=".urlencode('Cannot create import directory');
885 header('Location: '.$_SERVER['HTTP_REFERER']);
887 if (file_exists($full_filename)) @unlink($full_filename);
891 /* extract the entire archive into TR_COURSE_CONTENT . import/$course using the call back function to filter out php files */
893 $archive = new PclZip($_FILES['file']['tmp_name']);
895 if ($archive->extract( PCLZIP_OPT_PATH, $import_path,
896 PCLZIP_CB_PRE_EXTRACT, 'preImportCallBack') == 0) {
898 echo "error=".urlencode('Cannot unzip the package');
900 $msg->addError('IMPORT_FAILED');
901 echo 'Error : '.$archive->errorInfo(true);
903 FileUtility::clr_dir($import_path);
904 header('Location: '.$_SERVER['HTTP_REFERER']);
905 if (file_exists($full_filename)) @unlink($full_filename);
910 /* initialize DAO objects */
911 $coursesDAO = new CoursesDAO();
912 $contentDAO = new ContentDAO();
913 $testsQuestionsAssocDAO = new TestsQuestionsAssocDAO();
914 $contentTestsAssocDAO = new ContentTestsAssocDAO();
916 // get the course's max_quota
917 if (isset($_POST['_course_id']))
919 check_available_size($_POST['_course_id']);
922 $items = array(); /* all the content pages */
923 $order = array(); /* keeps track of the ordering for each content page */
924 $path = array(); /* the hierarchy path taken in the menu to get to the current item in the manifest */
925 $dependency_files = array(); /* the file path for the dependency files */
928 $items[content_id/resource_id] = array(
930 'real_content_id' // calculated after being inserted
936 $ims_manifest_xml = @file_get_contents($import_path.'imsmanifest.xml');
938 //scan for manifest xml if it's not on the top level.
939 if ($ims_manifest_xml === false){
940 $data = rscandir($import_path);
941 $manifest_array = array();
942 foreach($data as $scanned_file){
943 $scanned_file = realpath($scanned_file);
944 //change the file string to an array
945 $this_file_array = explode(DIRECTORY_SEPARATOR, $scanned_file);
946 if(empty($manifest_array)){
947 $manifest_array = $this_file_array;
949 $manifest_array = array_intersect_assoc($this_file_array, $manifest_array);
951 if (strpos($scanned_file, 'imsmanifest')!==false){
952 $ims_manifest_xml = @file_get_contents($scanned_file);
955 if ($ims_manifest_xml !== false){
956 $import_path = implode(DIRECTORY_SEPARATOR, $manifest_array) . DIRECTORY_SEPARATOR;
960 //if no imsmanifest.xml found in the entire package, throw error.
961 if ($ims_manifest_xml === false) {
962 $msg->addError('NO_IMSMANIFEST');
964 if (file_exists($import_path . 'atutor_backup_version')) {
965 $msg->addError('NO_IMS_BACKUP');
967 FileUtility::clr_dir($import_path);
969 if (isset($_GET['tile'])) {
970 header('Location: '.$_base_path.'tools/tile/index.php');
971 } else if ($oauth_import) {
972 echo "error=".urlencode('IMS manifest file does not appear to be valid');
974 header('Location: '.$_SERVER['HTTP_REFERER']);
976 if (file_exists($full_filename)) @unlink($full_filename);
980 $xml_parser = xml_parser_create();
982 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */
983 xml_set_element_handler($xml_parser, 'startElement', 'endElement');
984 xml_set_character_data_handler($xml_parser, 'characterData');
986 if (!xml_parse($xml_parser, $ims_manifest_xml, true)) {
987 die(sprintf("XML error: %s at line %d",
988 xml_error_string(xml_get_error_code($xml_parser)),
989 xml_get_current_line_number($xml_parser)));
991 xml_parser_free($xml_parser);
993 // Check if all the files exists in the manifest, iff it's a IMS CC package.
994 if ($content_type == 'IMS Common Cartridge') {
995 checkResources($import_path);
998 // Check if there are any errors during parsing.
999 if ($msg->containsErrors()) {
1000 if (isset($_GET['tile'])) {
1001 header('Location: '.$_base_path.'tools/tile/index.php');
1002 } else if ($oauth_import) {
1003 echo "error=".urlencode('Error at parsing IMS manifest file');
1005 header('Location: '.$_SERVER['HTTP_REFERER']);
1007 if (file_exists($full_filename)) @unlink($full_filename);
1011 // added by Cindy Li on Jan 10, 2010
1012 // generate a course_id if the import is not into an existing course
1013 if (!isset($_POST['_course_id']))
1015 if (isset($_POST['hide_course']))
1016 $access = 'private';
1020 if (isset($course_primary_lang) && $course_primary_lang != '')
1022 $langcode_and_charset = explode('-', $course_primary_lang);
1023 // $course_primary_lang = Utility::get3LetterLangCode($langcode_and_charset[0]);
1024 $course_primary_lang = $langcode_and_charset[0];
1026 $course_primary_lang = DEFAULT_LANGUAGE_CODE;
1028 $_course_id = $coursesDAO->Create($_SESSION['user_id'], 'top', $access, $course_title, $course_description,
1029 '', '', '', '', $course_primary_lang, '', '');
1031 check_available_size($_course_id);
1033 // insert author role into table "user_courses"
1034 $userCoursesDAO = new UserCoursesDAO();
1035 $userCoursesDAO->Create($_SESSION['user_id'], $_course_id, TR_USERROLE_AUTHOR, 0);
1037 else $_course_id = $_POST['_course_id'];
1039 // end of added by Cindy Li on Jan 10, 2010
1041 /* generate a unique new package base path based on the package file name and date as needed. */
1042 /* the package name will be the dir where the content for this package will be put, as a result */
1043 /* the 'content_path' field in the content table will be set to this path. */
1044 /* $package_base_name_url comes from the URL file name (NOT the file name of the actual file we open)*/
1045 if (!$package_base_name && $package_base_name_url) {
1046 $package_base_name = substr($package_base_name_url, -6);
1047 } else if (!$package_base_name) {
1048 $package_base_name = substr($_FILES['file']['name'], 0, -4);
1051 $package_base_name = strtolower($package_base_name);
1052 $package_base_name = str_replace(array('\'', '"', ' ', '|', '\\', '/', '<', '>', ':'), '_' , $package_base_name);
1053 $package_base_name = preg_replace("/[^A-Za-z0-9._\-]/", '', $package_base_name);
1055 $course_dir = TR_CONTENT_DIR.$_course_id.'/';
1057 if (is_dir($course_dir.$package_base_name)) {
1058 $package_base_name .= '_'.date('ymdHis');
1061 if ($package_base_path) {
1062 $package_base_path = implode('/', $package_base_path);
1063 } elseif (empty($package_base_path)){
1064 $package_base_path = '';
1067 if ($xml_base_path) {
1068 $package_base_path = $xml_base_path . $package_base_path;
1070 mkdir($import_path.$xml_base_path);
1071 $package_base_name = $xml_base_path . $package_base_name;
1074 /* get the top level content ordering offset */
1075 $order_offset = $contentDAO->getMaxOrdering($_course_id, 0);
1076 $lti_offset = array(); //since we don't need lti tools, the ordering needs to be subtracted
1078 //reorder the items stack
1079 $common_path = removeCommonPath($items);
1080 $items = rehash($items);
1081 //debug($items);exit;
1082 foreach ($items as $item_id => $content_info)
1084 //formatting field, default 1
1085 $content_formatting = 1; //CONTENT_TYPE_CONTENT
1087 //don't want to display glossary as a page
1088 if ($content_info['href']== $glossary_path . 'glossary.xml'){
1092 //if discussion tools, add it to the list of unhandled dts
1093 if ($content_info['type']=='imsdt_xmlv1p0'){
1094 //if it will be taken care after (has dependency), then move along.
1095 if (in_array($item_id, $avail_dt)){
1096 $lti_offset[$content_info['parent_content_id']]++;
1101 //handle the special case of cc import, where there is no content association. The resource should
1102 //still be imported.
1103 if(!isset($content_info['parent_content_id'])){
1104 //if this is a question bank
1105 if ($content_info['type']=="imsqti_xmlv1p2/imscc_xmlv1p0/question-bank"){
1106 addQuestions($content_info['href'], $content_info, $import_path);
1110 //if it has no title, most likely it is not a page but just a normal item, skip it
1111 if (!isset($content_info['title'])){
1115 //check dependency immediately, then handles it
1117 if (is_array($content_info['dependency']) && !empty($content_info['dependency'])){
1118 foreach($content_info['dependency'] as $dependency_ref){
1119 //check if this is a discussion tool dependency
1120 if ($items[$dependency_ref]['type']=='imsdt_xmlv1p0'){
1121 $items[$item_id]['forum'][$dependency_ref] = $items[$dependency_ref]['href'];
1123 //check if this is a QTI dependency
1124 if (strpos($items[$dependency_ref]['type'], 'imsqti_xmlv1p2/imscc_xmlv1p0') !== false){
1125 $items[$item_id]['tests'][$dependency_ref] = $items[$dependency_ref]['href'];
1132 if (preg_match('/^http.*:\/\//', trim($content_info['href'])) )
1134 $content = '<a href="'.$content_info['href'].'" target="_blank">'.$content_info['title'].'</a>';
1138 if ($content_type == 'IMS Common Cartridge'){
1139 //to handle import with purely images but nothing else
1140 //don't need a content base path for it.
1141 $content_new_path = $content_info['new_path'];
1142 $content_info['new_path'] = '';
1144 if (isset($content_info['href'], $xml_base_path)) {
1145 $content_info['href'] = $xml_base_path . $content_info['href'];
1147 if (!isset($content_info['href'])) {
1148 // this item doesn't have an identifierref. so create an empty page.
1149 // what we called a folder according to v1.2 Content Packaging spec
1153 $last_modified = date('Y-m-d H:i:s');
1155 //$file_info = @stat(TR_CONTENT_DIR . 'import/'.$_POST['_course_id'].'/'.$content_info['href']);
1156 $file_info = @stat($import_path.$content_info['href']);
1157 if ($file_info === false) {
1161 //$path_parts = pathinfo(TR_CONTENT_DIR . 'import/'.$_POST['_course_id'].'/'.$content_info['href']);
1162 $path_parts = pathinfo($import_path.$content_info['href']);
1163 $ext = strtolower($path_parts['extension']);
1165 $last_modified = date('Y-m-d H:i:s', $file_info['mtime']);
1167 if (in_array($ext, array('gif', 'jpg', 'bmp', 'png', 'jpeg'))) {
1168 /* this is an image */
1169 $content = '<img src="'.$content_info['href'].'" alt="'.$content_info['title'].'" />';
1170 } else if ($ext == 'swf') {
1172 /* Using default size of 550 x 400 */
1174 $content = '<object type="application/x-shockwave-flash" data="' . $content_info['href'] . '" width="550" height="400"><param name="movie" value="'. $content_info['href'] .'" /></object>';
1176 } else if ($ext == 'mov') {
1177 /* this is a quicktime movie */
1178 /* Using default size of 550 x 400 */
1180 $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>';
1182 } else if ($ext == 'mp3') {
1183 $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>';
1184 } else if (in_array($ext, array('wav', 'au'))) {
1185 $content = '<embed SRC="'.$content_info['href'].'" autostart="false" width="145" height="60"><noembed><bgsound src="'.$content_info['href'].'"></noembed></embed>';
1187 } else if (in_array($ext, array('txt', 'css', 'html', 'htm', 'csv', 'asc', 'tsv', 'xml', 'xsl'))) {
1188 if ($content_type == 'IMS Common Cartridge'){
1189 $content_info['new_path'] = $content_new_path;
1192 /* this is a plain text file */
1194 $content = file_get_contents($import_path.$content_info['href']);
1195 if ($content === false) {
1196 /* if we can't stat() it then we're unlikely to be able to read it */
1197 /* so we'll never get here. */
1201 // get the contents of the 'head' element
1202 $head .= ContentUtility::getHtmlHeadByTag($content, $html_head_tags);
1204 // Specifically handle eXe package
1205 // NOTE: THIS NEEDS WORK! TO FIND A WAY APPLY EXE .CSS FILES ONLY ON COURSE CONTENT PART.
1206 // NOW USE OUR OWN .CSS CREATED SOLELY FOR EXE
1207 $isExeContent = false;
1209 // check xml file in eXe package
1210 if (preg_match("/<organization[ ]*identifier=\"eXe*>*/", $ims_manifest_xml))
1212 $isExeContent = true;
1215 // use ATutor's eXe style sheet as the ones from eXe conflicts with ATutor's style sheets
1218 $head = preg_replace ('/(<style.*>)(.*)(<\/style>)/ms', '\\1@import url(/docs/exestyles.css);\\3', $head);
1221 // end of specifically handle eXe package
1223 $content = ContentUtility::getHtmlBody($content);
1224 if ($contains_glossary_terms)
1226 // replace glossary content package links to real glossary mark-up using [?] [/?]
1227 // refer to bug 3641, edited by Harris
1228 $content = preg_replace('/<a href="([.\w\d\s]+[^"]+)" target="body" class="at-term">([.\w\d\s&;"]+|.*)<\/a>/i', '[?]\\2[/?]', $content);
1231 /* potential security risk? */
1232 if ( strpos($content_info['href'], '..') === false && !preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $content_info['href'])) {
1233 // @unlink(TR_CONTENT_DIR . 'import/'.$_POST['_course_id'].'/'.$content_info['href']);
1236 // overwrite content if this is discussion tool.
1237 if ($content_info['type']=='imsdt_xmlv1p0'){
1238 $dt_parser = new DiscussionToolsParser();
1239 $xml_content = @file_get_contents($import_path . $content_info['href']);
1240 $dt_parser->parse($xml_content);
1241 $forum_obj = $dt_parser->getDt();
1242 $content = $forum_obj->getText();
1244 $dt_parser->close();
1247 /* non text file, and can't embed (example: PDF files) */
1248 $content = '<a href="'.$content_info['href'].'">'.$content_info['title'].'</a>';
1251 $content_parent_id = $cid;
1252 if ($content_info['parent_content_id'] !== 0) {
1253 $content_parent_id = $items[$content_info['parent_content_id']]['real_content_id'];
1254 //if it's not there, use $cid
1255 if (!$content_parent_id){
1256 $content_parent_id = $cid;
1261 if ($content_parent_id == $cid) {
1262 $my_offset = $order_offset;
1265 /* replace the old path greatest common denomiator with the new package path. */
1266 /* we don't use str_replace, b/c there's no knowing what the paths may be */
1267 /* we only want to replace the first part of the path.
1269 if(is_array($all_package_base_path)){
1270 $all_package_base_path = implode('/', $all_package_base_path);
1274 if ($common_path != ''
1275 && ($content_info['new_path'] === $common_path
1276 || substr($content_info['new_path'], strlen($common_path)))) {
1277 $content_info['new_path'] = $package_base_name . substr($content_info['new_path'], strlen($common_path));
1279 $content_info['new_path'] = $package_base_name . '/' . $content_info['new_path'];
1283 if ($content_info['type']=='imswl_xmlv1p0'){
1284 $weblinks_parser = new WeblinksParser();
1285 $xml_content = @file_get_contents($import_path . $content_info['href']);
1286 $weblinks_parser->parse($xml_content);
1287 $content_info['title'] = $weblinks_parser->getTitle();
1288 $content = $weblinks_parser->getUrl();
1289 $content_folder_type = CONTENT_TYPE_WEBLINK;
1290 $content_formatting = 2;
1294 //if this file is a test_xml, create a blank page instead, for imscc.
1295 if (preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $content_info['href'])
1296 || preg_match('/imsqti\_(.*)/', $content_info['type'])) {
1301 //check for content_type
1302 if ($content_formatting!=CONTENT_TYPE_WEBLINK){
1303 $content_folder_type = (!isset($content_info['type'])?CONTENT_TYPE_FOLDER:CONTENT_TYPE_CONTENT);
1306 $items[$item_id]['real_content_id'] = $contentDAO->Create($_course_id, intval($content_parent_id),
1307 ($content_info['ordering'] + $my_offset - $lti_offset[$content_info['parent_content_id']] + 1),
1308 0, $content_formatting, "", $content_info['new_path'], $content_info['title'],
1309 $content, $head, 1, $content_info['test_message'], $content_folder_type);
1312 /* get the tests associated with this content */
1313 if (!empty($items[$item_id]['tests']) || strpos($items[$item_id]['type'], 'imsqti_xmlv1p2/imscc_xmlv1p0') !== false){
1314 $qti_import = new QTIImport($import_path);
1315 if (isset($items[$item_id]['tests'])){
1316 $loop_var = $items[$item_id]['tests'];
1318 $loop_var = $items[$item_id]['file'];
1321 foreach ($loop_var as $array_id => $test_xml_file){
1322 //check if this item is the qti item object, or it is the content item obj
1323 //switch it to qti obj if it's content item obj
1324 if ($items[$item_id]['type'] == 'webcontent'){
1325 $item_qti = $items[$array_id];
1327 $item_qti = $items[$item_id];
1329 //call subrountine to add the questions.
1330 $qids = addQuestions($test_xml_file, $item_qti, $import_path);
1333 if ($test_title==''){
1334 $test_title = $content_info['title'];
1337 $tid = $qti_import->importTest($test_title);
1339 //associate question and tests
1340 foreach ($qids as $order=>$qid){
1341 if (isset($qti_import->weights[$order])){
1342 $weight = round($qti_import->weights[$order]);
1346 $new_order = $order + 1;
1347 $testsQuestionsAssocDAO->Create($tid, $qid, $weight, $new_order);
1351 //associate content and test
1352 $contentTestsAssocDAO->Create($items[$item_id]['real_content_id'], $tid);
1358 /* get the a4a related xml */
1359 if (isset($items[$item_id]['a4a_import_enabled']) && isset($items[$item_id]['a4a']) && !empty($items[$item_id]['a4a'])) {
1360 $a4a_import = new A4aImport($items[$item_id]['real_content_id']);
1361 $a4a_import->setRelativePath($items[$item_id]['new_path']);
1362 $a4a_import->importA4a($items[$item_id]['a4a']);
1365 // get the discussion tools (dependent to content)
1366 if (isset($items[$item_id]['forum']) && !empty($items[$item_id]['forum'])){
1367 foreach($items[$item_id]['forum'] as $forum_ref => $forum_link){
1368 $dt_parser = new DiscussionToolsParser();
1369 $dt_import = new DiscussionToolsImport();
1371 //if this forum has not been added, parse it and add it.
1372 if (!isset($added_dt[$forum_ref])){
1373 $xml_content = @file_get_contents($import_path . $forum_link);
1374 $dt_parser->parse($xml_content);
1375 $forum_obj = $dt_parser->getDt();
1376 $dt_import->import($forum_obj, $items[$item_id]['real_content_id'], $_course_id);
1377 $added_dt[$forum_ref] = $dt_import->getFid();
1381 } elseif ($items[$item_id]['type']=='imsdt_xmlv1p0'){
1382 //optimize this, repeated codes as above
1383 $dt_parser = new DiscussionToolsParser();
1384 $dt_import = new DiscussionToolsImport();
1385 $xml_content = @file_get_contents($import_path . $content_info['href']);
1386 $dt_parser->parse($xml_content);
1387 $forum_obj = $dt_parser->getDt();
1388 $dt_import->import($forum_obj, $items[$item_id]['real_content_id'], $_course_id);
1389 $added_dt[$item_id] = $dt_import->getFid();
1395 if ($package_base_path == '.') {
1396 $package_base_path = '';
1399 // create course directory
1400 if (!is_dir($course_dir)) {
1401 if (!@mkdir($course_dir, 0700)) {
1402 $msg->addError('IMPORTDIR_FAILED');
1408 //--- harris edit for path thing
1409 $file = $import_path.$common_path;
1410 if (is_dir($file)) {
1411 rename($file, TR_CONTENT_DIR.$_course_id.DIRECTORY_SEPARATOR.$package_base_name);
1414 //takes care of the condition where the whole package doesn't have any contents but question banks
1415 //also is the case of urls
1416 if(is_array($all_package_base_path)){
1417 $all_package_base_path = implode('/', $all_package_base_path);
1419 if(strpos($all_package_base_path, 'http:/')===false){
1420 if (rename($import_path.$all_package_base_path, $course_dir.$package_base_name) === false) {
1421 if (!$msg->containsErrors()) {
1422 if ($oauth_import) {
1423 echo "error=".urlencode('Cannot move lesson directory into content directory');
1425 $msg->addError('IMPORT_FAILED');
1432 FileUtility::clr_dir($import_path);
1434 if (file_exists($full_filename)) @unlink($full_filename);
1436 if ($oauth_import) {
1437 echo 'course_id='.$_course_id;
1439 if (!$msg->containsErrors()) {
1440 $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY');
1442 header('Location: ../course/index.php?_course_id='.$_course_id);