aicc.class.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. <?php //$id:$
  2. /**
  3. * Defines the AICC class, which is meant to contain the aicc items (nuclear elements)
  4. * @package dokeos.learnpath.aicc
  5. * @author Yannick Warnier <ywarnier@beeznest.org>
  6. * @license GNU/GPL - See Dokeos license directory for details
  7. */
  8. /**
  9. * Defines the "aicc" child of class "learnpath"
  10. * @package dokeos.learnpath.aicc
  11. */
  12. require_once('aiccItem.class.php');
  13. //require_once('aiccMetadata.class.php');
  14. //require_once('aiccOrganization.class.php');
  15. require_once('aiccResource.class.php');
  16. require_once('aiccBlock.class.php');
  17. class aicc extends learnpath {
  18. var $config = array();
  19. var $config_basename = ''; //the configuration files might be multiple and might have
  20. //funny names. We need to keep the name of that file while we
  21. //install the content.
  22. var $config_files = array();
  23. var $config_exts = array(
  24. 'crs'=>0, //Course description file (mandatory)
  25. 'au' =>0, //Assignable Unit file (mandatory)
  26. 'des'=>0, //Descriptor file (mandatory)
  27. 'cst'=>0, //Course structure file (mandatory)
  28. 'ore'=>0, //Objectives relationshops file (optional)
  29. 'pre'=>0, //Prerequisites file (optional)
  30. 'cmp'=>0 //Completion Requirements file (optional)
  31. );
  32. var $aulist = array();
  33. var $au_order_list = array();
  34. var $au_order_list_new_id = array();
  35. var $deslist = array();
  36. var $cstlist = array();
  37. var $orelist = array();
  38. var $subdir = ''; //path between the scorm/ directory and the config files e.g. maritime_nav/maritime_nav. This is the path that will be used in the lp_path when importing a package
  39. var $zipname = ''; //keeps the zipfile safe for the object's life so that we can use it if no title avail
  40. var $lastzipnameindex = 0; //keeps an index of the number of uses of the zipname so far
  41. var $config_encoding = 'ISO-8859-1';
  42. var $debug = 3;
  43. /**
  44. * Class constructor. Based on the parent constructor.
  45. * @param string Course code
  46. * @param integer Learnpath ID in DB
  47. * @param integer User ID
  48. */
  49. function aicc($course_code=null,$resource_id=null,$user_id=null) {
  50. if($this->debug>0){error_log('In aicc::aicc()',0);}
  51. if(!empty($course_code) and !empty($resource_id) and !empty($user_id))
  52. {
  53. parent::learnpath($course_code, $resource_id, $user_id);
  54. }else{
  55. //do nothing but still build the aicc object
  56. }
  57. }
  58. /**
  59. * Opens a resource
  60. * @param integer Database ID of the resource
  61. */
  62. function open($id)
  63. {
  64. if($this->debug>0){error_log('In aicc::open()',0);}
  65. // redefine parent method
  66. }
  67. /**
  68. * Parses a set of AICC config files and puts everything into the $config array
  69. * @param string Path to the config files dir on the system. If not defined, uses the base path of the course's scorm dir
  70. * @return array Structured array representing the config files' contents
  71. */
  72. function parse_config_files($dir='')
  73. {
  74. if($this->debug>0){error_log('New LP - In aicc::parse_config_files('.$dir.')',0);}
  75. if(empty($dir)){
  76. //get the path of the AICC config files dir
  77. $dir = $this->subdir;
  78. }
  79. if(is_dir($dir) and is_readable($dir))
  80. {
  81. // Now go through all the config files one by one and parse everything into
  82. // AICC objects.
  83. // The basename for the config files is stored in $this->config_basename
  84. // Parse the Course Description File (.crs) - ini-type
  85. $crs_file = $dir.'/'.$this->config_files['crs'];
  86. $crs_params = $this->parse_ini_file_quotes_safe($crs_file);
  87. echo '<pre>crs:'.print_r($crs_params,true).'</pre>';
  88. if($this->debug>1){error_log('New LP - In aicc::parse_config_files() - '.$crs_file.' has been parsed',0);}
  89. //CRS distribute crs params into the aicc object
  90. if(!empty($crs_params['course']['course_creator'])){
  91. $this->course_creator = mysql_real_escape_string($crs_params['course']['course_creator']);
  92. }
  93. if(!empty($crs_params['course']['course_id'])){
  94. $this->course_id = mysql_real_escape_string($crs_params['course']['course_id']);
  95. }
  96. if(!empty($crs_params['course']['course_system'])){
  97. $this->course_system = $crs_params['course']['course_system'];
  98. }
  99. if(!empty($crs_params['course']['course_title'])){
  100. $this->course_title = mysql_real_escape_string($crs_params['course']['course_title']);
  101. }
  102. if(!empty($crs_params['course']['course_level'])){
  103. $this->course_level = $crs_params['course']['course_level'];
  104. }
  105. if(!empty($crs_params['course']['max_fields_cst'])){
  106. $this->course_max_fields_cst = $crs_params['course']['max_fields_cst'];
  107. }
  108. if(!empty($crs_params['course']['max_fields_ort'])){
  109. $this->course_max_fields_ort = $crs_params['course']['max_fields_ort'];
  110. }
  111. if(!empty($crs_params['course']['total_aus'])){
  112. $this->course_total_aus = $crs_params['course']['total_aus'];
  113. }
  114. if(!empty($crs_params['course']['total_blocks'])){
  115. $this->course_total_blocks = $crs_params['course']['total_blocks'];
  116. }
  117. if(!empty($crs_params['course']['total_objectives'])){
  118. $this->course_total_objectives = $crs_params['course']['total_objectives'];
  119. }
  120. if(!empty($crs_params['course']['total_complex_objectives'])){
  121. $this->course_total_complex_objectives = $crs_params['course']['total_complex_objectives'];
  122. }
  123. if(!empty($crs_params['course']['version'])){
  124. $this->course_version = $crs_params['course']['version'];
  125. }
  126. if(!empty($crs_params['course_description'])){
  127. $this->course_description = mysql_real_escape_string($crs_params['course_description']);
  128. }
  129. // Parse the Descriptor File (.des) - csv-type
  130. $des_file = $dir.'/'.$this->config_files['des'];
  131. $des_params = $this->parse_csv_file($des_file);
  132. echo '<pre>des:'.print_r($des_params,true).'</pre>';
  133. if($this->debug>1){error_log('New LP - In aicc::parse_config_files() - '.$des_file.' has been parsed',0);}
  134. //distribute des params into the aicc object
  135. foreach($des_params as $des){
  136. //one AU in AICC is equivalent to one SCO in SCORM (scormItem class)
  137. $oDes = new aiccResource('config',$des);
  138. $this->deslist[$oDes->identifier] =& $oDes;
  139. }
  140. // Parse the Assignable Unit File (.au) - csv-type
  141. $au_file = $dir.'/'.$this->config_files['au'];
  142. $au_params = $this->parse_csv_file($au_file);
  143. echo '<pre>au:'.print_r($au_params,true).'</pre>';
  144. if($this->debug>1){error_log('New LP - In aicc::parse_config_files() - '.$au_file.' has been parsed',0);}
  145. //distribute au params into the aicc object
  146. foreach($au_params as $au){
  147. $oAu = new aiccItem('config',$au);
  148. $this->aulist[$oAu->identifier] =& $oAu;
  149. $this->au_order_list[] = $oAu->identifier;
  150. }
  151. // Parse the Course Structure File (.cst) - csv-type
  152. $cst_file = $dir.'/'.$this->config_files['cst'];
  153. $cst_params = $this->parse_csv_file($cst_file,',','"',true);
  154. echo '<pre>cst:'.print_r($cst_params,true).'</pre>';
  155. if($this->debug>1){error_log('New LP - In aicc::parse_config_files() - '.$cst_file.' has been parsed',0);}
  156. //distribute cst params into the aicc object
  157. foreach($cst_params as $cst){
  158. $oCst = new aiccBlock('config',$cst);
  159. $this->cstlist[$oCst->identifier] =& $oCst;
  160. }
  161. // Parse the Objectives Relationships File (.ore) - csv-type - if exists
  162. //TODO @TODO implement these objectives. For now they're just parsed
  163. if(!empty($this->config_files['ore'])){
  164. $ore_file = $dir.'/'.$this->config_files['ore'];
  165. $ore_params = $this->parse_csv_file($ore_file,',','"',true);
  166. //echo '<pre>ore:'.print_r($ore_params,true).'</pre>';
  167. if($this->debug>1){error_log('New LP - In aicc::parse_config_files() - '.$ore_file.' has been parsed',0);}
  168. //distribute ore params into the aicc object
  169. foreach($ore_params as $ore){
  170. $oOre = new aiccObjective('config',$ore);
  171. $this->orelist[$oOre->identifier] =& $oOre;
  172. }
  173. }
  174. // Parse the Prerequisites File (.pre) - csv-type - if exists
  175. if(!empty($this->config_files['pre'])){
  176. $pre_file = $dir.'/'.$this->config_files['pre'];
  177. $pre_params = $this->parse_csv_file($pre_file);
  178. //echo '<pre>pre:'.print_r($pre_params,true).'</pre>';
  179. if($this->debug>1){error_log('New LP - In aicc::parse_config_files() - '.$pre_file.' has been parsed',0);}
  180. //distribute pre params into the aicc object
  181. foreach($pre_params as $pre){
  182. //place a constraint on the corresponding block or AU
  183. if(in_array(strtolower($pre['structure_element']),array_keys($this->cstlist))){
  184. //if this references a block element
  185. $this->cstlist[strtolower($pre['structure_element'])]->prereq_string = strtolower($pre['prerequisite']);
  186. }
  187. if(in_array(strtolower($pre['structure_element']),array_keys($this->aulist))){
  188. //if this references a block element
  189. $this->aulist[strtolower($pre['structure_element'])]->prereq_string = strtolower($pre['prerequisite']);
  190. }
  191. }
  192. }
  193. // Parse the Completion Requirements File (.cmp) - csv-type - if exists
  194. //TODO @TODO implement this set of requirements (needs database changes)
  195. if(!empty($this->config_files['cmp'])){
  196. $cmp_file = $dir.'/'.$this->config_files['cmp'];
  197. $cmp_params = $this->parse_csv_file($cmp_file);
  198. //echo '<pre>cmp:'.print_r($cmp_params,true).'</pre>';
  199. if($this->debug>1){error_log('New LP - In aicc::parse_config_files() - '.$cmp_file.' has been parsed',0);}
  200. //distribute cmp params into the aicc object
  201. foreach($cmp_params as $cmp){
  202. //$oCmp = new aiccCompletionRequirements('config',$cmp);
  203. //$this->cmplist[$oCmp->identifier] =& $oCmp;
  204. }
  205. }
  206. }
  207. return $this->config;
  208. }
  209. /**
  210. * Import the aicc object (as a result from the parse_config_files function) into the database structure
  211. * @param string Unique course code
  212. * @return bool Returns -1 on error
  213. */
  214. function import_aicc($course_code){
  215. if($this->debug>0){error_log('New LP - In aicc::import_aicc('.$course_code.')',0);}
  216. //get table names
  217. $new_lp = 'lp';
  218. $new_lp_item = 'lp_item';
  219. //The previous method wasn't safe to get the database name, so do it manually with the course_code
  220. $sql = "SELECT * FROM ".Database::get_main_table(MAIN_COURSE_TABLE)." WHERE code='$course_code'";
  221. $res = api_sql_query($sql,__FILE__,__LINE__);
  222. if(Database::num_rows($res)<1){ error_log('New LP - Database for '.$course_code.' not found '.__FILE__.' '.__LINE__,0);return -1;}
  223. $row = Database::fetch_array($res);
  224. $dbname = Database::get_course_table_prefix().$row['db_name'].Database::get_database_glue();
  225. $new_lp = Database::get_course_table('lp');
  226. $new_lp_item = Database::get_course_table('lp_item');
  227. $this->config_encoding = "ISO-8859-1";
  228. $sql = "INSERT INTO $new_lp " .
  229. "(lp_type, name, ref, description, " .
  230. "path, force_commit, default_view_mod, default_encoding, " .
  231. "js_lib, content_maker)" .
  232. "VALUES " .
  233. "(3,'".$this->course_title."', '".$this->course_id."','".$this->course_description."'," .
  234. "'".$this->subdir."', 0, 'embedded', '".$this->config_encoding."'," .
  235. "'aicc_api.php','".$this->course_creator."')";
  236. if($this->debug>2){error_log('New LP - In import_aicc(), inserting path: '. $sql,0);}
  237. $res = api_sql_query($sql);
  238. $lp_id = Database::get_last_insert_id();
  239. $this->lp_id = $lp_id;
  240. $previous = 0;
  241. foreach($this->aulist as $identifier => $dummy)
  242. {
  243. $oAu =& $this->aulist[$identifier];
  244. echo "Item ".$oAu->identifier;
  245. $field_add = '';
  246. $value_add = '';
  247. if(!empty($oAu->masteryscore)){
  248. $field_add = 'mastery_score, ';
  249. $value_add = $oAu->masteryscore.',';
  250. }
  251. $title = $oAu->identifier;
  252. if(is_object($this->deslist[$identifier])){
  253. $title = $this->deslist[$identifier]->title;
  254. }
  255. $path = $oAu->path;
  256. //$max_score = $oAu->max_score //TODO check if special constraint exists for this item
  257. //$min_score = $oAu->min_score //TODO check if special constraint exists for this item
  258. $parent = 0; //TODO deal with parent
  259. $previous = 0;
  260. $prereq = $oAu->prereq_string;
  261. //$previous = (!empty($this->au_order_list_new_id[x])?$this->au_order_list_new_id[x]:0); //TODO deal with previous
  262. $sql_item = "INSERT INTO $new_lp_item " .
  263. "(lp_id,item_type,ref,title," .
  264. "path,min_score,max_score, $field_add" .
  265. "parent_item_id,previous_item_id,next_item_id," .
  266. "prerequisite,display_order) " .
  267. "VALUES " .
  268. "($lp_id, 'au','".$oAu->identifier."','".$title."'," .
  269. "'$path',0,100, $value_add" .
  270. "$parent, $previous, 0, " .
  271. "'$prereq', 0" .
  272. ")";
  273. $res_item = api_sql_query($sql_item);
  274. if($this->debug>1){error_log('New LP - In aicc::import_aicc() - inserting item : '.$sql_item.' : '.mysql_error(),0);}
  275. $item_id = Database::get_last_insert_id();
  276. //now update previous item to change next_item_id
  277. if($previous != 0){
  278. $upd = "UPDATE $new_lp_item SET next_item_id = $item_id WHERE id = $previous";
  279. $upd_res = api_sql_query($upd);
  280. //update previous item id
  281. }
  282. $previous = $item_id;
  283. }
  284. }
  285. /**
  286. * Imports a zip file (presumably AICC) into the Dokeos structure
  287. * @param string Zip file info as given by $_FILES['userFile']
  288. * @return string Absolute path to the AICC config files directory or empty string on error
  289. */
  290. function import_package($zip_file_info,$current_dir = '')
  291. {
  292. if($this->debug>0){error_log('In aicc::import_package('.print_r($zip_file_info,true).',"'.$current_dir.'") method',0);}
  293. //ini_set('error_log','E_ALL');
  294. $maxFilledSpace = 1000000000;
  295. $zip_file_path = $zip_file_info['tmp_name'];
  296. $zip_file_name = $zip_file_info['name'];
  297. if($this->debug>0){error_log('New LP - aicc::import_package() - Zip file path = '.$zip_file_path.', zip file name = '.$zip_file_name,0);}
  298. $course_rel_dir = api_get_course_path().'/scorm'; //scorm dir web path starting from /courses
  299. $course_sys_dir = api_get_path(SYS_COURSE_PATH).$course_rel_dir; //absolute system path for this course
  300. $current_dir = replace_dangerous_char(trim($current_dir),'strict'); //current dir we are in, inside scorm/
  301. if($this->debug>0){error_log('New LP - aicc::import_package() - Current_dir = '.$current_dir,0);}
  302. //$uploaded_filename = $_FILES['userFile']['name'];
  303. //get name of the zip file without the extension
  304. if($this->debug>0){error_log('New LP - aicc::import_package() - Received zip file name: '.$zip_file_path,0);}
  305. $file_info = pathinfo($zip_file_name);
  306. $filename = $file_info['basename'];
  307. $extension = $file_info['extension'];
  308. $file_base_name = str_replace('.'.$extension,'',$filename); //filename without its extension
  309. $this->zipname = $file_base_name; //save for later in case we don't have a title
  310. if($this->debug>0){error_log('New LP - aicc::import_package() - Base file name is : '.$file_base_name,0);}
  311. $new_dir = replace_dangerous_char(trim($file_base_name),'strict');
  312. $this->subdir = $new_dir;
  313. if($this->debug>0){error_log('New LP - aicc::import_package() - Subdir is first set to : '.$this->subdir,0);}
  314. /*
  315. if( check_name_exist($course_sys_dir.$current_dir."/".$new_dir) )
  316. {
  317. $dialogBox = get_lang('FileExists');
  318. $stopping_error = true;
  319. }
  320. */
  321. $zipFile = new pclZip($zip_file_path);
  322. // Check the zip content (real size and file extension)
  323. $zipContentArray = $zipFile->listContent();
  324. $package_type=''; //the type of the package. Should be 'aicc' after the next few lines
  325. $package = ''; //the basename of the config files (if 'courses.crs' => 'courses')
  326. $at_root = false; //check if the config files are at zip root
  327. $config_dir = ''; //the directory in which the config files are. May remain empty
  328. $files_found = array();
  329. $subdir_isset = false;
  330. //the following loop should be stopped as soon as we found the right config files (.crs, .au, .des and .cst)
  331. foreach($zipContentArray as $thisContent)
  332. {
  333. if ( preg_match('~.(php.*|phtml)$~i', $thisContent['filename']) )
  334. {
  335. //if a php file is found, do not authorize (security risk)
  336. if($this->debug>1){error_log('New LP - aicc::import_package() - Found unauthorized file: '.$thisContent['filename'],0);}
  337. return api_failure::set_failure('php_file_in_zip_file');
  338. }elseif(preg_match('?.*/aicc/$?',$thisContent['filename'])){
  339. //if a directory named 'aicc' is found, package type = aicc, but continue
  340. //because we need to find the right AICC files
  341. if($this->debug>1){error_log('New LP - aicc::import_package() - Found aicc directory: '.$thisContent['filename'],0);}
  342. $package_type = 'aicc';
  343. }else{
  344. //else, look for one of the files we're searching for (something.crs case insensitive)
  345. $res = array();
  346. if(preg_match('?^(.*)\.(crs|au|des|cst|ore|pre|cmp)$?i',$thisContent['filename'],$res))
  347. {
  348. if($this->debug>1){error_log('New LP - aicc::import_package() - Found AICC config file: '.$thisContent['filename'].'. Now splitting: '.$res[1].' and '.$res[2],0);}
  349. if($thisContent['filename'] == basename($thisContent['filename'])){
  350. if($this->debug>2){error_log('New LP - aicc::import_package() - '.$thisContent['filename'].' is at root level',0);}
  351. $at_root = true;
  352. if(!is_array($files_found[$res[1]])){
  353. $files_found[$res[1]] = $this->config_exts; //initialise list of expected extensions (defined in class definition)
  354. }
  355. $files_found[$res[1]][strtolower($res[2])] = $thisContent['filename'];
  356. $subdir_isset = true;
  357. }else{
  358. if(!$subdir_isset){
  359. if(preg_match('?^.*/aicc$?i',dirname($thisContent['filename']))){
  360. echo "Cutting subdir<br/>";
  361. $this->subdir .= '/'.substr(dirname($thisContent['filename']),0,-5);
  362. }else{
  363. echo "Not cutting subdir<br/>";
  364. $this->subdir .= '/'.dirname($thisContent['filename']);
  365. }
  366. $subdir_isset = true;
  367. }
  368. if($this->debug>2){error_log('New LP - aicc::import_package() - '.$thisContent['filename'].' is not at root level - recording subdir '.$this->subdir,0);}
  369. $config_dir = dirname($thisContent['filename']); //just the relative directory inside scorm/
  370. if(!is_array($files_found[basename($res[1])])){
  371. $files_found[basename($res[1])] = $this->config_exts;
  372. }
  373. $files_found[basename($res[1])][strtolower($res[2])] = basename($thisContent['filename']);
  374. }
  375. $package_type = 'aicc';
  376. }else{
  377. if($this->debug>3){error_log('New LP - aicc::import_package() - File '.$thisContent['filename'].' didnt match any check',0);}
  378. }
  379. }
  380. $realFileSize += $thisContent['size'];
  381. }
  382. if($this->debug>2){error_log('New LP - aicc::import_package() - $files_found: '.print_r($files_found,true),0);}
  383. if($this->debug>1){error_log('New LP - aicc::import_package() - Package type is now '.$package_type,0);}
  384. $mandatory = false;
  385. foreach($files_found as $file_name => $file_exts){
  386. $temp = (
  387. !empty($files_found[$file_name]['crs'])
  388. AND !empty($files_found[$file_name]['au'])
  389. AND !empty($files_found[$file_name]['des'])
  390. AND !empty($files_found[$file_name]['cst'])
  391. );
  392. if($temp){
  393. if($this->debug>1){error_log('New LP - aicc::import_package() - Found all config files for '.$file_name,0);}
  394. $mandatory = true;
  395. $package = $file_name;
  396. //store base config file name for reuse in parse_config_files()
  397. $this->config_basename = $file_name;
  398. //store filenames for reuse in parse_config_files()
  399. $this->config_files = $files_found[$file_name];
  400. //get out, we only want one config files set
  401. break;
  402. }
  403. }
  404. if($package_type== '' OR $mandatory!=true)
  405. // && defined('CHECK_FOR_AICC') && CHECK_FOR_AICC)
  406. {
  407. return api_failure::set_failure('not_aicc_content');
  408. }
  409. if (! enough_size($realFileSize, $course_sys_dir, $maxFilledSpace) )
  410. {
  411. return api_failure::set_failure('not_enough_space');
  412. }
  413. // it happens on Linux that $new_dir sometimes doesn't start with '/'
  414. if($new_dir[0] != '/')
  415. {
  416. $new_dir='/'.$new_dir;
  417. }
  418. //cut trailing slash
  419. if($new_dir[strlen($new_dir)-1] == '/')
  420. {
  421. $new_dir=substr($new_dir,0,-1);
  422. }
  423. /*
  424. --------------------------------------
  425. Uncompressing phase
  426. --------------------------------------
  427. */
  428. /*
  429. The first version, using OS unzip, is not used anymore
  430. because it does not return enough information.
  431. We need to process each individual file in the zip archive to
  432. - add it to the database
  433. - parse & change relative html links
  434. */
  435. if (PHP_OS == 'Linux' && ! get_cfg_var('safe_mode') && false) // *** UGent, changed by OC ***
  436. {
  437. // Shell Method - if this is possible, it gains some speed
  438. //check availability of 'unzip' first!
  439. exec("unzip -d \"".$course_sys_dir."".$new_dir." ".$zip_file_path);
  440. if($this->debug>=1){error_log('New LP - found Linux system, using unzip',0);}
  441. }
  442. elseif(is_dir($course_sys_dir.$new_dir) OR @mkdir($course_sys_dir.$new_dir))
  443. {
  444. // PHP method - slower...
  445. if($this->debug>=1){error_log('New LP - Changing dir to '.$course_sys_dir.$new_dir,0);}
  446. $saved_dir = getcwd();
  447. chdir($course_sys_dir.$new_dir);
  448. $unzippingState = $zipFile->extract();
  449. for($j=0;$j<count($unzippingState);$j++)
  450. {
  451. $state=$unzippingState[$j];
  452. //TODO fix relative links in html files (?)
  453. $extension = strrchr($state["stored_filename"], ".");
  454. //if($this->debug>1){error_log('New LP - found extension '.$extension.' in '.$state['stored_filename'],0);}
  455. }
  456. if(!empty($new_dir))
  457. {
  458. $new_dir = $new_dir.'/';
  459. }
  460. //rename files, for example with \\ in it
  461. if($dir=@opendir($course_sys_dir.$new_dir))
  462. {
  463. if($this->debug==1){error_log('New LP - Opened dir '.$course_sys_dir.$new_dir,0);}
  464. while($file=readdir($dir))
  465. {
  466. if($file != '.' && $file != '..')
  467. {
  468. $filetype="file";
  469. if(is_dir($course_sys_dir.$new_dir.$file)) $filetype="folder";
  470. //TODO RENAMING FILES CAN BE VERY DANGEROUS AICC-WISE, avoid that as much as possible!
  471. //$safe_file=replace_dangerous_char($file,'strict');
  472. $safe_file = str_replace('\\','/',$file);
  473. if($safe_file != $file){
  474. //@rename($course_sys_dir.$new_dir,$course_sys_dir.'/'.$safe_file);
  475. $mydir = dirname($course_sys_dir.$new_dir.$safe_file);
  476. if(!is_dir($mydir)){
  477. $mysubdirs = split('/',$mydir);
  478. $mybasedir = '/';
  479. foreach($mysubdirs as $mysubdir){
  480. if(!empty($mysubdir)){
  481. $mybasedir = $mybasedir.$mysubdir.'/';
  482. if(!is_dir($mybasedir)){
  483. @mkdir($mybasedir);
  484. if($this->debug==1){error_log('New LP - Dir '.$mybasedir.' doesnt exist. Creating.',0);}
  485. }
  486. }
  487. }
  488. }
  489. @rename($course_sys_dir.$new_dir.$file,$course_sys_dir.$new_dir.$safe_file);
  490. if($this->debug==1){error_log('New LP - Renaming '.$course_sys_dir.$new_dir.$file.' to '.$course_sys_dir.$new_dir.$safe_file,0);}
  491. }
  492. //set_default_settings($course_sys_dir,$safe_file,$filetype);
  493. }
  494. }
  495. closedir($dir);
  496. chdir($saved_dir);
  497. }
  498. }else{
  499. return '';
  500. }
  501. return $course_sys_dir.$new_dir.$config_dir;
  502. }
  503. /**
  504. * Sets the proximity setting in the database
  505. * @param string Proximity setting
  506. */
  507. function set_proximity($proxy=''){
  508. if($this->debug>0){error_log('In aicc::set_proximity('.$proxy.') method',0);}
  509. $lp = $this->get_id();
  510. if($lp!=0){
  511. $tbl_lp = Database::get_course_table('lp');
  512. $sql = "UPDATE $tbl_lp SET content_local = '$proxy' WHERE id = ".$lp;
  513. $res = api_sql_query($sql);
  514. return $res;
  515. }else{
  516. return false;
  517. }
  518. }
  519. /**
  520. * Sets the content maker setting in the database
  521. * @param string Proximity setting
  522. */
  523. function set_maker($maker=''){
  524. if($this->debug>0){error_log('In aicc::set_maker method('.$maker.')',0);}
  525. $lp = $this->get_id();
  526. if($lp!=0){
  527. $tbl_lp = Database::get_course_table('lp');
  528. $sql = "UPDATE $tbl_lp SET content_maker = '$maker' WHERE id = ".$lp;
  529. $res = api_sql_query($sql);
  530. return $res;
  531. }else{
  532. return false;
  533. }
  534. }
  535. /**
  536. * Exports the current AICC object's files as a zip. Excerpts taken from learnpath_functions.inc.php::exportpath()
  537. * @param integer Learnpath ID (optional, taken from object context if not defined)
  538. */
  539. function export_zip($lp_id=null){
  540. if($this->debug>0){error_log('In aicc::export_zip method('.$lp_id.')',0);}
  541. if(empty($lp_id)){
  542. if(!is_object($this))
  543. {
  544. return false;
  545. }
  546. else{
  547. $id = $this->get_id();
  548. if(empty($id)){
  549. return false;
  550. }
  551. else{
  552. $lp_id = $this->get_id();
  553. }
  554. }
  555. }
  556. //error_log('New LP - in export_zip()',0);
  557. //zip everything that is in the corresponding scorm dir
  558. //write the zip file somewhere (might be too big to return)
  559. require_once (api_get_path(LIBRARY_PATH)."fileUpload.lib.php");
  560. require_once (api_get_path(LIBRARY_PATH)."fileManage.lib.php");
  561. require_once (api_get_path(LIBRARY_PATH)."document.lib.php");
  562. require_once (api_get_path(LIBRARY_PATH)."pclzip/pclzip.lib.php");
  563. require_once ("learnpath_functions.inc.php");
  564. $tbl_lp = Database::get_course_table('lp');
  565. $_course = Database::get_course_info(api_get_course_id());
  566. $sql = "SELECT * FROM $tbl_lp WHERE id=".$lp_id;
  567. $result = api_sql_query($sql, __FILE__, __LINE__);
  568. $row = mysql_fetch_array($result);
  569. $LPname = $row['path'];
  570. $list = split('/',$LPname);
  571. $LPnamesafe = $list[0];
  572. //$zipfoldername = '/tmp';
  573. //$zipfoldername = '../../courses/'.$_course['directory']."/temp/".$LPnamesafe;
  574. $zipfoldername = api_get_path('SYS_COURSE_PATH').$_course['directory']."/temp/".$LPnamesafe;
  575. $scormfoldername = api_get_path('SYS_COURSE_PATH').$_course['directory']."/scorm/".$LPnamesafe;
  576. $zipfilename = $zipfoldername."/".$LPnamesafe.".zip";
  577. //Get a temporary dir for creating the zip file
  578. //error_log('New LP - cleaning dir '.$zipfoldername,0);
  579. deldir($zipfoldername); //make sure the temp dir is cleared
  580. $res = mkdir($zipfoldername);
  581. //error_log('New LP - made dir '.$zipfoldername,0);
  582. //create zipfile of given directory
  583. $zip_folder = new PclZip($zipfilename);
  584. $zip_folder->create($scormfoldername.'/', PCLZIP_OPT_REMOVE_PATH, $scormfoldername.'/');
  585. //$zipfilename = '/var/www/dokeos-comp/courses/TEST2/scorm/example_document.html';
  586. //this file sending implies removing the default mime-type from php.ini
  587. //DocumentManager :: file_send_for_download($zipfilename, true, $LPnamesafe.".zip");
  588. DocumentManager :: file_send_for_download($zipfilename, true);
  589. // Delete the temporary zip file and directory in fileManage.lib.php
  590. my_delete($zipfilename);
  591. my_delete($zipfoldername);
  592. return true;
  593. }
  594. /**
  595. * Gets a resource's path if available, otherwise return empty string
  596. * @param string Resource ID as used in resource array
  597. * @return string The resource's path as declared in config file course.crs
  598. */
  599. function get_res_path($id){
  600. if($this->debug>0){error_log('In aicc::get_res_path('.$id.') method',0);}
  601. $path = '';
  602. if(isset($this->resources[$id])){
  603. $oRes =& $this->resources[$id];
  604. $path = @$oRes->get_path();
  605. }
  606. return $path;
  607. }
  608. /**
  609. * Gets a resource's type if available, otherwise return empty string
  610. * @param string Resource ID as used in resource array
  611. * @return string The resource's type as declared in the assignable unit (.au) file
  612. */
  613. function get_res_type($id){
  614. if($this->debug>0){error_log('In aicc::get_res_type('.$id.') method',0);}
  615. $type = '';
  616. if(isset($this->resources[$id])){
  617. $oRes =& $this->resources[$id];
  618. $temptype = $oRes->get_scorm_type();
  619. if(!empty($temptype)){
  620. $type = $temptype;
  621. }
  622. }
  623. return $type;
  624. }
  625. /**
  626. * Gets the default organisation's title
  627. * @return string The organization's title
  628. */
  629. function get_title(){
  630. if($this->debug>0){error_log('In aicc::get_title() method',0);}
  631. $title = '';
  632. if(isset($this->config['organizations']['default'])){
  633. $title = $this->organizations[$this->config['organizations']['default']]->get_name();
  634. }elseif(count($this->organizations)==1){
  635. //this will only get one title but so we don't need to know the index
  636. foreach($this->organizations as $id => $value){
  637. $title = $this->organizations[$id]->get_name();
  638. break;
  639. }
  640. }
  641. return $title;
  642. }
  643. /**
  644. * //TODO @TODO implement this function to restore items data from a set of AICC config files,
  645. * updating the existing table... This will prove very useful in case initial data
  646. * from config files were not imported well enough
  647. */
  648. function reimport_aicc(){
  649. if($this->debug>0){error_log('In aicc::reimport_aicc() method',0);}
  650. //query current items list
  651. //get the identifiers
  652. //parse the config files
  653. //match both
  654. //update DB accordingly
  655. return true;
  656. }
  657. /**
  658. * Static function to parse AICC ini files.
  659. * Based on work by sinedeo at gmail dot com published on php.net (parse_ini_file())
  660. * @param string File path
  661. * @return array Structured array
  662. */
  663. function parse_ini_file_quotes_safe($f)
  664. {
  665. $null = "";
  666. $r=$null;
  667. $sec=$null;
  668. $f=@file($f);
  669. for ($i=0;$i<@count($f);$i++)
  670. {
  671. $newsec=0;
  672. $w=@trim($f[$i]);
  673. if ($w)
  674. {
  675. if ((!$r) or ($sec))
  676. {
  677. if ((@substr($w,0,1)=="[") and (@substr($w,-1,1))=="]")
  678. {
  679. $sec=@substr($w,1,@strlen($w)-2);
  680. $newsec=1;
  681. }
  682. }
  683. if (!$newsec)
  684. {
  685. $w=@explode("=",$w);
  686. $k=@trim($w[0]);
  687. unset($w[0]);
  688. $v=@trim(@implode("=",$w));
  689. if ((@substr($v,0,1)=="\"") and (@substr($v,-1,1)=="\""))
  690. {
  691. $v=@substr($v,1,@strlen($v)-2);
  692. }
  693. if ($sec)
  694. {
  695. if(strtolower($sec)=='course_description'){//special case
  696. $r[strtolower($sec)]=$k;
  697. }else{
  698. $r[strtolower($sec)][strtolower($k)]=$v;
  699. }
  700. } else
  701. {
  702. $r[strtolower($k)]=$v;
  703. }
  704. }
  705. }
  706. }
  707. return $r;
  708. }
  709. /**
  710. * Static function that parses CSV files into simple arrays, based on a function
  711. * by spam at cyber-space dot nl published on php.net (fgetcsv())
  712. * @param string Filepath
  713. * @param string CSV delimiter
  714. * @param string CSV enclosure
  715. * @param boolean Might one field name happen more than once on the same line? (then split by comma in the values)
  716. * @return array Simple structured array
  717. */
  718. function parse_csv_file($f,$delim=',',$enclosure='"',$multiples=false)
  719. {
  720. $data = file_get_contents($f);
  721. $enclosed=false;
  722. $fldcount=0;
  723. $linecount=0;
  724. $fldval='';
  725. for($i=0;$i<strlen($data);$i++)
  726. {
  727. $chr=$data{$i};
  728. switch($chr)
  729. {
  730. case $enclosure:
  731. if($enclosed&&$data{$i+1}==$enclosure)
  732. {
  733. $fldval.=$chr;
  734. ++$i; //skip next char
  735. }
  736. else
  737. $enclosed=!$enclosed;
  738. break;
  739. case $delim:
  740. if(!$enclosed)
  741. {
  742. $ret_array[$linecount][$fldcount++]=$fldval;
  743. $fldval='';
  744. }
  745. else
  746. $fldval.=$chr;
  747. break;
  748. case "\r":
  749. if(!$enclosed&&$data{$i+1}=="\n")
  750. continue;
  751. case "\n":
  752. if(!$enclosed)
  753. {
  754. $ret_array[$linecount++][$fldcount]=$fldval;
  755. $fldcount=0;
  756. $fldval='';
  757. }
  758. else
  759. $fldval.=$chr;
  760. break;
  761. case "\\r":
  762. if(!$enclosed&&$data{$i+1}=="\\n")
  763. continue;
  764. case "\\n":
  765. if(!$enclosed)
  766. {
  767. $ret_array[$linecount++][$fldcount]=$fldval;
  768. $fldcount=0;
  769. $fldval='';
  770. }
  771. else
  772. $fldval.=$chr;
  773. break;
  774. default:
  775. $fldval.=$chr;
  776. }
  777. }
  778. if($fldval){
  779. $ret_array[$linecount][$fldcount]=$fldval;
  780. }
  781. //transform the array to use the first line as titles
  782. $titles = array();
  783. $ret_ret_array = array();
  784. foreach($ret_array as $line_idx => $line){
  785. if($line_idx == 0){
  786. $titles = $line;
  787. }else{
  788. $ret_ret_array[$line_idx] = array();
  789. foreach($line as $idx=>$val)
  790. {
  791. if($multiples && !empty($ret_ret_array[$line_idx][strtolower($titles[$idx])])){
  792. $ret_ret_array[$line_idx][strtolower($titles[$idx])].=",".$val;
  793. }else{
  794. $ret_ret_array[$line_idx][strtolower($titles[$idx])]=$val;
  795. }
  796. }
  797. }
  798. }
  799. return $ret_ret_array;
  800. }
  801. }
  802. ?>