learnpath_functions.inc.php 100 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * This is a function library for the learning path.
  5. *
  6. * Due to the face that the learning path has been built upon the resoucelinker,
  7. * naming conventions have changed at least 2 times. You can see here in order the :
  8. * 1. name used in the first version of the resourcelinker
  9. * 2. name used in the first version of the LP
  10. * 3. name used in the second (current) version of the LP
  11. *
  12. * 1. 2. 3.
  13. * Category = Chapter = Module
  14. * Item (?) = Item = Step
  15. *
  16. * @author Denes Nagy <darkden@evk.bke.hu>, main author
  17. * @author Roan Embrechts, some code cleaning
  18. * @author Yannick Warnier <yannick.warnier@beeznest.com>, multi-level learnpath behaviour + new SCORM tool
  19. * @access public
  20. * @package chamilo.learnpath
  21. * @todo rename functions to coding conventions: not deleteitem but delete_item, etc
  22. * @todo rewrite functions to comply with phpDocumentor
  23. * @todo remove code duplication
  24. */
  25. use \ChamiloSession as Session;
  26. /**
  27. * This function deletes an item
  28. * @param integer $id: the item we want to delete
  29. * @return boolean True if item was deleted, false if not found or error
  30. */
  31. function deleteitem($id) {
  32. $course_id = api_get_course_int_id();
  33. $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
  34. $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
  35. // Get the display order for this item before it is deleted.
  36. $sql = "SELECT display_order, parent_item_id FROM $tbl_lp_item WHERE c_id = $course_id AND id=$id";
  37. $result = Database::query($sql);
  38. if (Database::num_rows($result) == 0) {
  39. return false;
  40. }
  41. $row = Database::fetch_row($result);
  42. $display_order = $row[0];
  43. $parent_item_id = $row[1];
  44. // Delete the item.
  45. $sql = "DELETE FROM $tbl_learnpath_item WHERE c_id = $course_id AND id='$id'";
  46. $result = Database::query($sql);
  47. if ($result === false) {
  48. return false;
  49. }
  50. // Update the other items and chapters.
  51. $sql = "UPDATE $tbl_learnpath_item SET display_order = display_order-1 WHERE c_id = $course_id AND display_order > $display_order AND parent_item_id = $parent_item_id";
  52. $result = Database::query($sql);
  53. $sql = "UPDATE $tbl_learnpath_chapter SET display_order = display_order-1 WHERE c_id = $course_id AND display_order > $display_order AND parent_item_id = $parent_item_id";
  54. $result = Database::query($sql);
  55. return true;
  56. }
  57. /**
  58. * This function deletes a module(chapter) and all its steps(items).
  59. *
  60. * @param integer id of the chapter we want to delete
  61. * @return boolean True on success and false if not found or error
  62. */
  63. function deletemodule($parent_item_id) {
  64. global $learnpath_id;
  65. $course_id = api_get_course_int_id();
  66. $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
  67. $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
  68. // Added for multi-level behaviour - slightly recursive.
  69. $sql = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND lp_id=$learnpath_id";
  70. $result = Database::query($sql);
  71. while ($row = Database::fetch_array($result)) {
  72. if ($row['parent_item_id'] == $parent_item_id) {
  73. // Delete every subchapter.
  74. if (deletemodule($row['id']) === false) {
  75. return false;
  76. }
  77. }
  78. }
  79. // Get this chapter's display order.
  80. $sql = "SELECT display_order, parent_item_id FROM $tbl_learnpath_chapter
  81. WHERE c_id = $course_id AND id=$parent_item_id and lp_id=$learnpath_id";
  82. $result = Database::query($sql);
  83. if (Database::num_rows($result) == 0) {
  84. return false;
  85. }
  86. $row = Database::fetch_row($result);
  87. $display_order = $row[0];
  88. $parent_id = $row[1];
  89. // Delete the chapter itself.
  90. $sql = "DELETE FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND (id=$parent_item_id and lp_id=$learnpath_id)";
  91. $result = Database::query($sql);
  92. // Delete items from that chapter.
  93. $sql2 = "DELETE FROM $tbl_learnpath_item WHERE c_id = $course_id AND parent_item_id=$parent_item_id";
  94. $result = Database::query($sql2);
  95. // Update all other chapters accordingly.
  96. $sql = "UPDATE $tbl_learnpath_item SET display_order = display_order-1 WHERE c_id = $course_id AND display_order > $display_order AND parent_item_id = $parent_id";
  97. $result = Database::query($sql);
  98. $sql = "UPDATE $tbl_learnpath_chapter SET display_order = display_order-1 WHERE c_id = $course_id AND display_order > $display_order AND parent_item_id = $parent_id";
  99. $result = Database::query($sql);
  100. return true;
  101. }
  102. /**
  103. * This function deletes an entire path.
  104. *
  105. * @param integer $id: the path we want to delete
  106. * @return void
  107. */
  108. function deletepath($path_id) {
  109. $tbl_learnpath_main = Database :: get_course_table(TABLE_LEARNPATH_MAIN);
  110. $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
  111. $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
  112. $course_id = api_get_course_int_id();
  113. $sql = "DELETE FROM $tbl_learnpath_main WHERE c_id = $course_id AND lp_id='$path_id'";
  114. $result = Database::query($sql);
  115. //@TODO check how this function is used before uncommenting the following
  116. //also delete all elements inside that path
  117. $sql = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND lp_id = $path_id";
  118. $result = Database::query($sql);
  119. while ($row = Database::fetch_array($result)) {
  120. deletemodule($row['id']);
  121. }
  122. }
  123. /**
  124. * This function moves an item.
  125. *
  126. * @param string $direction: move the given chapter up or down
  127. * @param integer Item ID
  128. * @param integer $moduleid: the id of the chapter the element resides in
  129. * @return boolean Returns false on error
  130. * @note With this new version, the moveitem deals with items AND directories (not the base-level modules). This is a lot more complicated but is a temporary step towards new database structure as 'everything is an item'
  131. */
  132. function moveitem($direction, $id, $moduleid, $type = 'item') {
  133. global $learnpath_id;
  134. $course_id = api_get_course_int_id();
  135. $tbl_learnpath_item = Database::get_course_table(TABLE_LEARNPATH_ITEM);
  136. $tbl_learnpath_chapter = Database::get_course_table(TABLE_LEARNPATH_CHAPTER);
  137. $tree = get_learnpath_tree($learnpath_id);
  138. $orig_order = 0;
  139. $orig_type = '';
  140. $orig_id = $id;
  141. foreach ($tree[$moduleid] as $row) {
  142. // If this is the element we want (be it a chapter or an item), get its data.
  143. if (($row['id'] == $id) && ($row['type'] == $type)) {
  144. $orig_order = $row['display_order'];
  145. $orig_type = $row['type'];
  146. break;
  147. }
  148. }
  149. $dest_order = 0;
  150. $dest_type = '';
  151. $dest_id = 0;
  152. if ($direction == 'up') {
  153. if (!empty ($tree[$moduleid][$orig_order - 1])) {
  154. $dest_order = $orig_order - 1;
  155. $dest_type = $tree[$moduleid][$orig_order - 1]['type'];
  156. $dest_id = $tree[$moduleid][$orig_order - 1]['id'];
  157. } else {
  158. return false;
  159. }
  160. } else {
  161. // Move down.
  162. if (!empty ($tree[$moduleid][$orig_order + 1])) {
  163. $dest_order = $orig_order + 1;
  164. $dest_type = $tree[$moduleid][$orig_order + 1]['type'];
  165. $dest_id = $tree[$moduleid][$orig_order + 1]['id'];
  166. } else {
  167. return false;
  168. }
  169. }
  170. $sql1 = '';
  171. $sql2 = '';
  172. if ($orig_type == 'chapter') {
  173. $sql1 = "UPDATE $tbl_learnpath_chapter SET display_order = ".$dest_order." WHERE c_id = $course_id AND (id=$orig_id and parent_item_id=$moduleid)";
  174. } elseif ($orig_type == 'item') {
  175. $sql1 = "UPDATE $tbl_learnpath_item SET display_order = ".$dest_order." WHERE c_id = $course_id AND (id=$orig_id and parent_item_id=$moduleid)";
  176. } else {
  177. return false;
  178. }
  179. if ($dest_type == 'chapter') {
  180. $sql2 = "UPDATE $tbl_learnpath_chapter SET display_order = ".$orig_order." WHERE c_id = $course_id AND (id='$dest_id' and parent_item_id=$moduleid)";
  181. } elseif ($dest_type == 'item') {
  182. $sql2 = "UPDATE $tbl_learnpath_item SET display_order = ".$orig_order." WHERE c_id = $course_id AND (id='$dest_id' and parent_item_id=$moduleid)";
  183. } else {
  184. return false;
  185. }
  186. Database::query($sql1);
  187. Database::query($sql2);
  188. }
  189. /**
  190. * This function moves a module (also called chapter or category).
  191. *
  192. * @param string $direction: move the given chapter up or down
  193. * @param integer $id: the id of the chapter we want to move
  194. * @return void
  195. */
  196. function movemodule($direction, $id) {
  197. global $learnpath_id;
  198. $course_id = api_get_course_int_id();
  199. $tbl_learnpath_chapter = Database::get_course_table(TABLE_LEARNPATH_CHAPTER);
  200. if ($direction == 'up') {
  201. $sortDirection = 'DESC';
  202. } else {
  203. $sortDirection = 'ASC';
  204. }
  205. // Select all chapters of first level (parent_item_id = 0).
  206. $sql = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND (lp_id=$learnpath_id AND parent_item_id = 0) ORDER BY display_order $sortDirection";
  207. $result = Database::query($sql);
  208. $previousrow = '';
  209. // See similar comment in moveitem() function.
  210. // @TODO: this only works for chapters in multi-level mode. Why not gather
  211. // this function and moveitem to keep only one multi-uses function?
  212. while ($row = Database::fetch_array($result)) {
  213. // Step 2: Performing the move (only happens when passed trhough step 1 at least once).
  214. if (!empty ($this_cat_order)) {
  215. $next_cat_order = $row['display_order'];
  216. $next_cat_id = $row['id'];
  217. $sql1 = "UPDATE $tbl_learnpath_chapter SET display_order = '$next_cat_order' WHERE c_id = $course_id AND (id='$this_cat_id' and lp_id=$learnpath_id)";
  218. $sql2 = "UPDATE $tbl_learnpath_chapter SET display_order = '$this_cat_order' WHERE c_id = $course_id AND (id='$next_cat_id' and lp_id=$learnpath_id)";
  219. Database::query($sql1);
  220. Database::query($sql2);
  221. unset ($this_cat_order);
  222. unset ($next_cat_order);
  223. unset ($next_cat_id);
  224. break;
  225. }
  226. // Step 1: Looking for the order of the row we want to move.
  227. if ($row['id'] == $id) {
  228. $this_cat_order = $row['display_order'];
  229. $this_cat_id = $id;
  230. }
  231. }
  232. }
  233. /**
  234. * Inserts a new element in a learnpath table (item or chapter)
  235. * @param string Element type ('chapter' or 'item')
  236. * @param string Chapter name
  237. * @param string Chapter description (optional)
  238. * @param integer Parent chapter ID (default: 0)
  239. * @param integer Learnpath ID
  240. * @param mixed If type 'item', then array(prereq_id=>value, prereq_..)
  241. * @return integer The new chapter ID, or false on failure
  242. * @TODO Finish this function before it is used. Currently only chapters can be added using it.
  243. * @note This function is currently never used!
  244. */
  245. function insert_item($type = 'item', $name, $chapter_description = '', $parent_id = 0, $learnpath_id = 0, $params = null) {
  246. $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
  247. $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
  248. $course_id = api_get_course_int_id();
  249. // Getting the last order number from the chapters table, in this learnpath, for the parent chapter given.
  250. $sql = "SELECT * FROM $tbl_learnpath_chapter
  251. WHERE c_id = $course_id AND lp_id=$learnpath_id AND parent_item_id = $parent_id
  252. ORDER BY display_order DESC";
  253. $result = Database::query($sql);
  254. $row = Database::fetch_array($result);
  255. $last_chapter_order = $row['display_order'];
  256. // Getting the last order number of the items.
  257. $sql = "SELECT * FROM $tbl_learnpath_item
  258. WHERE c_id = $course_id AND parent_item_id = $parent_id
  259. ORDER BY display_order DESC";
  260. $result = Database::query($sql);
  261. $row = Database::fetch_array($result);
  262. $last_item_order = $row['display_order'];
  263. $new_order = max($last_chapter_order, $last_item_order) + 1;
  264. if ($type === 'chapter') {
  265. $sql = "INSERT INTO $tbl_learnpath_chapter (c_id, lp_id, chapter_name, chapter_description, display_order)
  266. VALUES ( $course_id,
  267. '".domesticate($learnpath_id)."',
  268. '".domesticate(htmlspecialchars($name))."',
  269. '".domesticate(htmlspecialchars($chapter_description))."',
  270. $new_order )";
  271. $result = Database::query($sql);
  272. if ($result === false) {
  273. return false;
  274. }
  275. $id = Database :: insert_id();
  276. } elseif ($type === 'item') {
  277. $sql = "INSERT INTO $tbl_learnpath_item (c_id, parent_item_id, item_type, display_order) VALUES
  278. ($course_id, '".domesticate($parent_id)."','".domesticate(htmlspecialchars($type))."', $new_order )";
  279. $result = Database::query($sql);
  280. if ($result === false) {
  281. return false;
  282. }
  283. $id = Database :: insert_id();
  284. }
  285. return $id;
  286. }
  287. /**
  288. * This function returns an array with all the learnpath categories/chapters
  289. * @return array List of learnpath chapter titles
  290. */
  291. function array_learnpath_categories() {
  292. $course_id = api_get_course_int_id();
  293. global $learnpath_id;
  294. $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
  295. $sql = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND (lp_id=$learnpath_id) ORDER BY display_order ASC";
  296. $result = Database::query($sql);
  297. while ($row = Database::fetch_array($result)) {
  298. $array_learnpath_categories[] = array($row['id'], $row['chapter_name']);
  299. }
  300. //$array_learnpath_categories = array($array_learnpath_categories_name, $array_learnpath_categories_id);
  301. return $array_learnpath_categories;
  302. }
  303. /**
  304. * Displays the learnpath chapters(=modules,categories) and their contents.
  305. * @param integer Chapter ID to display now (enables recursive behaviour)
  306. * @param array The array as returned by get_learnpath_tree, with all the elements of a learnpath compiled and structured into the array, by chapter id
  307. * @param integer Level (the depth of the call - helps in display)
  308. * @todo eliminate all global $lang declarations, use get_lang, improve structure.
  309. * @author Denes Nagy
  310. * @author Roan Embrechts
  311. * @author Yannick Warnier <yannick.warnier@beeznest.com> - complete redesign for multi-level learnpath chapters
  312. */
  313. function display_learnpath_chapters($parent_item_id = 0, $tree = array (), $level = 0) {
  314. //error_log('New LP - In learnpath_functions::display_learnpath_chapters', 0);
  315. global $color2;
  316. global $xml_output;
  317. global $learnpath_id;
  318. $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
  319. // @todo: coding standards: Language variables are CaMMelCaSe, all other variables should use the underscoring method.
  320. $lg_move_down = get_lang('LearnpathMoveDown');
  321. $lg_move_up = get_lang('LearnpathMoveUp');
  322. $lg_edit_learnpath_module = get_lang('LearnpathEditModule');
  323. $lg_delete_learnpath_module = get_lang('LearnpathDeleteModule');
  324. $lg_nochapters = get_lang('LearnpathNoChapters');
  325. $lg_prerequisites = get_lang('LearnpathPrerequisites');
  326. $lg_prerequisites_limit = get_lang('LearnpathPrerequisitesLimit');
  327. $lg_add_learnpath_item = get_lang('LearnpathAddItem');
  328. $lg_title_and_desc = get_lang('LearnpathTitleAndDesc');
  329. $lg_change_order = get_lang('LearnpathChangeOrder');
  330. $lg_add_prereqi = get_lang('LearnpathAddPrereqi');
  331. $lg_add_title_and_desc = get_lang('LearnpathAddTitleAndDesc');
  332. $lg_delete = get_lang('Delete');
  333. if ($parent_item_id === 0) {
  334. // This is the first time we use the function, define the tree and display learnpath name.
  335. $tree = get_learnpath_tree($learnpath_id);
  336. $num_modules = count($tree);
  337. //$num_modules = Database::num_rows($result);
  338. if ($num_modules == 0) {
  339. // do not diplay useless information
  340. //echo "<tr><td>&nbsp;$lg_nochapters</td></tr>";
  341. } else {
  342. echo " <tr align='center' valign='top'><td><b>&nbsp;$lg_title_and_desc </b></td>\n"." <td><b>&nbsp;$lg_add_learnpath_item </b></td>\n";
  343. if (is_prereq($learnpath_id)) {
  344. echo " <td bgcolor='#ddddee'><b>&nbsp;$lg_prerequisites_limit </b></td>\n";
  345. } else {
  346. echo " <td><b>&nbsp;$lg_prerequisites </b></td>\n";
  347. }
  348. echo " <td colspan='2'><b>&nbsp;$lg_change_order </b></td><td><b>&nbsp;$lg_add_prereqi </b></td>\n"." <td><b>&nbsp;$lg_add_title_and_desc </b></td><td><b>&nbsp;$lg_delete </b></td>\n"." </tr>\n";
  349. }
  350. }
  351. $i = 1;
  352. $counter = 0;
  353. $num_modules = count($tree[$parent_item_id]);
  354. //while ($row = Database::fetch_array($result))
  355. if (isset ($tree[$parent_item_id])) {
  356. foreach ($tree[$parent_item_id] as $row) {
  357. if ($row['item_type'] === 'dokeos_chapter') {
  358. $xml_output .= "<chapter>";
  359. $xml_output .= "<chaptertitle>".$row['title']."</chaptertitle>";
  360. $xml_output .= "<chapterdescription>".$row['description']."</chapterdescription>";
  361. $counter ++;
  362. if (($counter % 2) == 0) {
  363. $oddclass = 'row_odd';
  364. } else {
  365. $oddclass = 'row_even';
  366. }
  367. //echo '<tr class="'.$oddclass.'">'."\n".' <td>'.str_repeat("&nbsp;&gt;", $level)."<img src='../img/documents.gif' alt='folder'/><a href='".api_get_self()."?lp_id=$learnpath_id&item_id={$row['id']}&action=add&type=learnpathitem&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9'><b>&nbsp;".$row['title']."</b></a>"."<br /><i><div align='justify'>&nbsp;".str_repeat("&nbsp;&nbsp;&nbsp;", $level)."</i></td>\n".' <td align="center"><a href="'.api_get_self()."?lp_id=$learnpath_id&item_id={$row['id']}&action=add&type=learnpathitem&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9"><img src='../img/0.gif' width='13' height='13' border='0' title='$lg_add_learnpath_item'></a></td>\n"." <td";
  368. echo '<tr class="'.$oddclass.'">'."\n".' <td>'.str_repeat("&nbsp;&gt;", $level)."<img src='../img/documents.gif' alt='folder'/><a href='".api_get_self()."?lp_id=$learnpath_id&parent_item_id=".$row['id']."&action=add_sub_item'><b>&nbsp;".$row['title']."</b></a>"."<br /><i><div align='justify'>&nbsp;".str_repeat("&nbsp;&nbsp;&nbsp;", $level)."</i></td>\n".' <td align="center"><a href="'.api_get_self()."?lp_id=$learnpath_id&parent_item_id=".$row['id']."&action=add_sub_item\"><img src='../img/0.gif' width='13' height='13' border='0' title='$lg_add_learnpath_item'></a></td>\n"." <td";
  369. if (is_prereq($learnpath_id)) {
  370. echo " bgcolor='#ddddee'";
  371. }
  372. echo ">".$row['prerequisite']."</td>\n";
  373. // Showing the edit, delete and move icons.
  374. if (api_is_allowed_to_edit()) {
  375. $myaction = 'move_item';
  376. if ($i < $num_modules) {
  377. // If we are still under the number of chapters in this section, show "move down".
  378. //echo " <td align=center>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=".$myaction."&amp;direction=down&amp;moduleid=".$parent_item_id."&amp;id=".$row['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/down.gif\" border=\"0\" title=\"$lg_move_down\">"."</a></td>\n";
  379. echo " <td align=center>"."<a href='".api_get_self()."?lp_id=$learnpath_id&action=".$myaction."&direction=down&moduleid=".$parent_item_id."&id=".$row['id']."'>"."<img src=\"../img/down.gif\" border=\"0\" title=\"$lg_move_down\">"."</a></td>\n";
  380. } else {
  381. echo ' <td align="center">&nbsp;</td>'."\n";
  382. }
  383. if ($i > 1) {
  384. //echo ' <td align="center">'."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=".$myaction."&amp;direction=up&amp;moduleid=".$parent_item_id."&amp;id=".$row['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/up.gif\" border=\"0\" title=\"$lg_move_up\">"."</a>"."</td>\n";
  385. echo ' <td align="center">'."<a href='".api_get_self()."?lp_id=$learnpath_id&action=".$myaction."&direction=up&moduleid=".$parent_item_id."&id=".$row['id']."'>"."<img src=\"../img/up.gif\" border=\"0\" title=\"$lg_move_up\">"."</a>"."</td>\n";
  386. } else {
  387. echo ' <td align="center">&nbsp;</td>'."\n";
  388. }
  389. echo " <td align='center'>&nbsp;</td>\n";
  390. echo " <td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&action=edititem&id=".$row['id']."'>"."<img src=\"../img/edit.gif\" border=\"0\" title=\"$lg_edit_learnpath_module\">"."</a>"."</td>\n";
  391. echo " <td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&action=delete_item&id=".$row['id']."' onclick=\"javascript: return confirmation('".$row['title']."');\">"."<img src=\"../img/delete.gif\" border=\"0\" title=\"$lg_delete_learnpath_module\">"."</a>"."</td>\n";
  392. }
  393. echo "</tr>\n";
  394. $i ++;
  395. $xml_output .= "<items>";
  396. //display_learnpath_items($row['id']);
  397. display_learnpath_chapters($row['id'], $tree, $level + 1);
  398. $xml_output .= "</items>";
  399. $xml_output .= "</chapter>";
  400. } else //if //($row['item_type'] === 'item')
  401. {
  402. $row_items = $row;
  403. echo "<tr>\n <td colspan='2' valign='top'>";
  404. //require 'resourcelinker.inc.php';
  405. display_addedresource_link_in_learnpath($row_items['item_type'], $row_items['ref'], '', $row_items['id'], 'builder', 'icon', $level);
  406. if ($row_items['description']) {
  407. echo "<div align='justify'>&nbsp;&nbsp;&nbsp;{$row_items['description']}";
  408. }
  409. echo "</td>";
  410. if (is_prereq($learnpath_id)) {
  411. echo '<td bgcolor="#EEEEFF">';
  412. } else {
  413. echo "<td>";
  414. }
  415. if (api_is_allowed_to_edit()) {
  416. if ($row_items['prerequisite'] != '') {
  417. $prereq = $row_items['prerequisite'];
  418. //if ($row_items['prereq_type'] == 'i') {
  419. // item
  420. $sql_items2 = "SELECT * FROM $tbl_lp_item WHERE id='$prereq'"; // Check if prereq has been deleted.
  421. $result_items2 = Database::query($sql_items2);
  422. $number_items2 = Database::num_rows($result_items2);
  423. if ($number_items2 == 0) {
  424. echo get_lang('PrerequisiteDeletedError');
  425. }
  426. $row_items2 = Database::fetch_array($result_items2);
  427. display_addedresource_link_in_learnpath($row_items2['item_type'], $row_items2['ref'], '', $row_items2['id'], 'builder', '', 0);
  428. if ((($row_items2['item_type'] == TOOL_QUIZ) or ($row_items2['item_type'] == 'HotPotatoes')) and ($row_items['prerequisite'])) {
  429. //echo "&nbsp;({$row_items2['title']})";
  430. }
  431. //}
  432. /*
  433. if ($row_items['prereq_type'] == 'c') {
  434. // chapter
  435. $sql_items2 = "SELECT * FROM $tbl_lp_item WHERE id='$prereq' AND item_type='dokeos_chapter'"; // Check if prereq has been deleted.
  436. $result_items2 = Database::query($sql_items2);
  437. $number_items2 = Database::num_rows($result_items2);
  438. if ($number_items2 == 0) {
  439. echo "<font color='red'>$lg_prereq_deleted_error</font>";
  440. }
  441. $row_items2 = Database::fetch_array($result_items2);
  442. echo " {$row_items2['title']}";
  443. }*/
  444. }
  445. echo "</font></td>";
  446. $xml_output .= "<element_type>".$row_items['item_type']."</element_type>";
  447. $xml_output .= "<element_id>".$row_items['item_id']."</element_id>";
  448. // Move
  449. if ($i < $num_modules) {
  450. echo "<td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=moveitem&amp;type=item&amp;direction=down&amp;moduleid=".$parent_item_id."&amp;id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/down.gif\" border=\"0\" title=\"$lg_move_down\">"."</a>"."</td>";
  451. } else {
  452. echo "<td width='30' align='center'>&nbsp;</td>";
  453. }
  454. if ($i > 1) {
  455. echo "<td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=moveitem&amp;type=item&amp;direction=up&amp;moduleid=".$parent_item_id."&amp;id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/up.gif\" border=\"0\" title=\"$lg_move_up\">"."</a>";
  456. } else {
  457. echo "<td width='30' align='center'>&nbsp;</td>";
  458. }
  459. echo "</td>"."<td align='center'>";
  460. // Edit prereq
  461. echo "<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=edititemprereq&amp;id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/scormpre.gif\" border=\"0\" title=\"$lg_add_prereq\">"."</a>"."</td>";
  462. // Edit
  463. echo "<td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=edititem&amp;id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/edit.gif\" border=\"0\" title=\"$lg_edit_learnpath_item\">"."</a>"."</td>";
  464. // Delete
  465. echo "<td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&action=deleteitem&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/delete.gif\" border=\"0\" title=\"$lg_delete_learnpath_item\" onclick=\"javascript: return confirmation('".$row_items['item_type']."');\">"."</a>";
  466. }
  467. $i ++;
  468. echo "</td></tr>";
  469. }
  470. }
  471. }
  472. }
  473. /**
  474. * Displays the learning path items/steps.
  475. * @param integer Category ID
  476. * @return void
  477. * @todo eliminate all global $lang declarations, use get_lang, improve structure.
  478. */
  479. function display_learnpath_items($categoryid) {
  480. global $xml_output;
  481. global $lg_prerequisites, $lg_move_down, $lg_move_up, $lg_edit_learnpath_item, $lg_delete_learnpath_item, $learnpath_id, $lg_add_prereq, $lg_prereq_deleted_error, $lg_pre_short, $langThisItem;
  482. $course_id = api_get_course_int_id();
  483. $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
  484. $sql_items = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND parent_item_id='$categoryid' ORDER BY display_order ASC";
  485. $result_items = Database::query($sql_items);
  486. $number_items = Database::num_rows($result_items);
  487. $i = 1;
  488. while ($row_items = Database::fetch_array($result_items)) {
  489. echo "<tr><td colspan='2' valign='top'>";
  490. display_addedresource_link_in_learnpath($row_items['item_type'], $row_items['ref'], '', $row_items['id'], 'builder', 'icon');
  491. if ($row_items['description']) {
  492. echo "<div align='justify'><font color='#999999'>&nbsp;&nbsp;&nbsp;{$row_items['description']}</font>";
  493. }
  494. echo "</td>";
  495. if (is_prereq($learnpath_id)) {
  496. echo '<td bgcolor="#EEEEFF">';
  497. } else {
  498. echo "<td>";
  499. }
  500. if (api_is_allowed_to_edit()) {
  501. //error_log('Is allowed to edit item'.$row_items['id'], 0);
  502. // TODO: Fix by adding true prerequisites parsing (and cycle through).
  503. // Over simplification here, we transform prereq_id field into prerequisite field.
  504. if ($row_items['prerequisite'] != '') {
  505. $prereq = $row_items['prerequisite'];
  506. //if ($row_items['prereq_type'] == 'i') {
  507. // item
  508. $sql_items2 = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND id='$prereq'"; // Check if prereq has been deleted.
  509. $result_items2 = Database::query($sql_items2);
  510. $number_items2 = Database::num_rows($result_items2);
  511. if ($number_items2 == 0) {
  512. echo "<font color=red>$lg_prereq_deleted_error</font>";
  513. }
  514. $row_items2 = Database::fetch_array($result_items2);
  515. display_addedresource_link_in_learnpath($row_items2['item_type'], $row_items2['ref'], '', $row_items2['id'], 'builder', '');
  516. if ((($row_items2['item_type'] == 'Exercise') or ($row_items2['item_type'] == 'HotPotatoes')) and ($row_items['prerequisites'])) {
  517. echo "&nbsp;({$row_items2['title']})";
  518. }
  519. //}
  520. /*if ($row_items['prereq_type'] == 'c') {
  521. // chapter
  522. $sql_items2 = "SELECT * FROM $tbl_learnpath_chapter WHERE id='$prereq'"; //check if prereq has been deleted
  523. $result_items2 = Database::query($sql_items2);
  524. $number_items2 = Database::num_rows($result_items2);
  525. if ($number_items2 == 0) {
  526. echo "<font color=red>$lg_prereq_deleted_error</font>";
  527. }
  528. $row_items2 = Database::fetch_array($result_items2);
  529. echo " {$row_items2['chapter_name']}";
  530. }*/
  531. }
  532. echo "</font></td>";
  533. $xml_output .= "<element_type>".$row_items['item_type']."</element_type>";
  534. $xml_output .= "<element_id>".$row_items['id']."</element_id>";
  535. // Move
  536. if ($i < $number_items) {
  537. echo "<td align='center'><a href='".api_get_self()."?lp_id=$learnpath_id&action=moveitem&direction=down&moduleid=".$categoryid."&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/down.gif\" border=\"0\" title=\"$lg_move_down\"></a></td>";
  538. } else {
  539. echo "<td width='30' align='center'>&nbsp;</td>";
  540. }
  541. if ($i > 1) {
  542. echo "<td align='center'><a href='".api_get_self()."?lp_id=$learnpath_id&action=moveitem&direction=up&moduleid=".$categoryid."&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/up.gif\" border=\"0\" title=\"$lg_move_up\"></a>";
  543. } else {
  544. echo "<td width='30' align='center'>&nbsp;</td>";
  545. }
  546. echo "</td><td align='center'>";
  547. // Edit prereq
  548. echo "<a href='".api_get_self()."?lp_id=$learnpath_id&action=edititemprereq&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/scormpre.gif\" border=\"0\" title=\"$lg_add_prereq\"></a></td>";
  549. // Edit
  550. echo "<td align='center'><a href='".api_get_self()."?lp_id=$learnpath_id&action=edititem&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/edit.gif\" border=\"0\" title=\"$lg_edit_learnpath_item\"></a></td>";
  551. // Delete
  552. echo "<td align='center'>";
  553. echo "<a href='".api_get_self()."?lp_id=$learnpath_id&action=deleteitem&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/delete.gif\" border=\"0\" title=\"$lg_delete_learnpath_item\" onclick=\"javascript: return confirmation('".$langThisItem."');\"></a>";
  554. }
  555. $i ++;
  556. echo "</td></tr>";
  557. }
  558. }
  559. /**
  560. * This function returns the items belonging to the chapter that contains the given item (brother items)
  561. * @param integer Item id
  562. * @return array Table containing the items
  563. */
  564. function learnpath_items($itemid) {
  565. global $xml_output;
  566. $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
  567. $course_id = api_get_course_int_id();
  568. $sql_items = "SELECT parent_item_id FROM $tbl_lp_item WHERE c_id = $course_id AND id='$itemid'";
  569. $moduleid_sql = Database::query($sql_items);
  570. $moduleid_array = Database::fetch_array($moduleid_sql); // First row of the results.
  571. $moduleid = $moduleid_array['parent_item_id'];
  572. $sql_items = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND parent_item_id='$moduleid' ORDER BY display_order ASC";
  573. $result_items = Database::query($sql_items);
  574. $ar = Database::fetch_array($result_items);
  575. while ($ar != '') {
  576. $result[] = $ar;
  577. $ar = Database::fetch_array($result_items);
  578. }
  579. return $result;
  580. }
  581. /**
  582. * This function returns the chapters belonging to the path that contais the given chapter (brother chapters)
  583. * @param integer Learnpath id
  584. * @return array Table containing the chapters
  585. */
  586. function learnpath_chapters($learnpath_id) {
  587. global $xml_output, $learnpath_id;
  588. $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
  589. $course_id = api_get_course_int_id();
  590. $sql_items = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id='$learnpath_id' AND item_type='dokeos_chapter' ORDER BY display_order ASC";
  591. //$sql_items = "SELECT * FROM $tbl_learnpath_chapter WHERE lp_id='$learnpath_id' ORDER BY display_order ASC";
  592. $result_items = Database::query($sql_items);
  593. $ar = Database::fetch_array($result_items);
  594. while ($ar != '') {
  595. $result[] = $ar;
  596. $ar = Database::fetch_array($result_items);
  597. }
  598. return $result;
  599. }
  600. /**
  601. * This function tells if a learnpath contains items which are prerequisite to other items
  602. * @param integer Learnpath id
  603. * @return boolean True if this learnpath contains an item which is a prerequisite to something
  604. */
  605. function is_prereq($learnpath_id) {
  606. global $xml_output;
  607. $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
  608. $course_id = api_get_course_int_id();
  609. $prereq = false;
  610. $sql_items = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id='$learnpath_id' AND parent_item_id=0 ORDER BY display_order ASC";
  611. $result_items = Database::query($sql_items);
  612. while ($ar = Database::fetch_array($result_items)) {
  613. $c = $ar['id'];
  614. $sql_items2 = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id = $learnpath_id AND parent_item_id='$c' ORDER BY display_order ASC";
  615. $result_items2 = Database::query($sql_items2);
  616. while ($ar2 = Database::fetch_array($result_items2)) {
  617. if ($ar2['prerequisite'] != '') {
  618. $prereq = true;
  619. }
  620. }
  621. }
  622. return ($prereq);
  623. }
  624. /**
  625. * This function returns the prerequisite sentence
  626. * @param integer Item ID
  627. * @return string Prerequisite warning text
  628. */
  629. function prereqcheck($id_in_path) {
  630. // 1. Initialise and import working vars.
  631. global $learnpath_id, $_user;
  632. global $langPrereqToEnter, $langPrereqTestLimit1, $langPrereqTestLimit2, $langPrereqTestLimitNow, $langPrereqFirstNeedTo, $langPrereqModuleMinimum1, $langPrereqModuleMinimum2;
  633. $tbl_learnpath_user = Database :: get_course_table(TABLE_LEARNPATH_USER);
  634. $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
  635. $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
  636. $course_id = api_get_course_int_id();
  637. // 2. Initialise return value.
  638. $prereq = false;
  639. // 3. Get item data from the database.
  640. $sql_items = "SELECT * FROM $tbl_learnpath_item WHERE c_id = $course_id AND id='$id_in_path'";
  641. $result_items = Database::query($sql_items);
  642. $row = Database::fetch_array($result_items);
  643. // 4. Check prerequisite's type.
  644. if ($row['prereq_type'] == 'i') {
  645. // 4.a If prerequisite is of type 'i' (item):
  646. // 4.a.1 Get data ready for use.
  647. $id_in_path3 = $row['prereq_id'];
  648. $prereq_limit = $row['prereq_completion_limit'];
  649. // 4.a.2 Get data from the user-item relation.
  650. if ($_user['user_id'] == '') {
  651. $user_id = '0';
  652. } else {
  653. $user_id = $_user['user_id'];
  654. }
  655. $sql_items3 = "SELECT * FROM $tbl_learnpath_user WHERE c_id = $course_id AND (learnpath_item_id='$id_in_path3' and user_id=$user_id)";
  656. $result_items3 = Database::query($sql_items3);
  657. $row3 = Database::fetch_array($result_items3);
  658. // 4.a.3 Get the link that needs to be shown for the current item (not the prereq)
  659. $stepname = display_addedresource_link_in_learnpath($row['item_type'], $row['ref'], '', $id_in_path, 'builder', 'nolink');
  660. // This is the step we want to open.
  661. $stepname = trim($stepname); // Removing occasional line breaks and white spaces
  662. // 4.a.4 Get the prerequisite item.
  663. $sql6 = "SELECT * FROM $tbl_learnpath_item WHERE (id='$id_in_path3')";
  664. $result6 = Database::query($sql6);
  665. $row6 = Database::fetch_array($result6);
  666. // 4.a.5 Get a link to the prerequisite item.
  667. $prereqname = display_addedresource_link_in_learnpath($row6['item_type'], $row6['ref'], '', $id_in_path3, 'builder', 'nolink'); //this is the prereq of the step we want to open
  668. // 4.a.5 Initialise limit value.
  669. $limitok = true;
  670. // 4.a.6 Get prerequisite limit.
  671. if ($prereq_limit) {
  672. // 4.a.6.a If the completion limit exists.
  673. if ($row3['score'] < $prereq_limit) {
  674. // 4.a.6.a.a If the completion limit hasn't been reached, then display the corresponding message.
  675. $prereq = $langPrereqToEnter.$stepname.$langPrereqTestLimit1."$prereq_limit".$langPrereqTestLimit2.$prereqname.". (".$langPrereqTestLimitNow.$row3['score'].")";
  676. } else {
  677. // 4.a.6.a.b The completion limit has been reached. Prepare to return false (no prereq hanging).
  678. $prereq = false;
  679. }
  680. } else {
  681. // 4.a.6.b If the completion limit doesn't exist.
  682. if ($row3['status'] == 'completed' or $row3['status'] == 'passed') {
  683. // 4.a.6.b.a If the prerequisite status is 'completed'.
  684. $prereq = false;
  685. } else {
  686. // 4.a.6.b.b The prerequisite status is not 'completed', return corresponding message.
  687. $prereq = $langPrereqToEnter.$stepname.$langPrereqFirstNeedTo.$prereqname.'.';
  688. }
  689. }
  690. } elseif ($row['prereq_type'] == 'c') {
  691. // 4.b If prerequisite is of type 'c' (chapter).
  692. // 4.b.1 Get data ready to use.
  693. $id_in_path2 = $row['prereq_id'];
  694. // 4.b.2 Get all items in the prerequisite chapter.
  695. $sql_items3 = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND parent_item_id='$id_in_path2'";
  696. $result_items3 = Database::query($sql_items3);
  697. $allcompleted = true;
  698. while ($row3 = Database::fetch_array($result_items3)) {
  699. // 4.b.3 Cycle through items in the prerequisite chapter.
  700. // 4.b.3.1 Get data ready to use.
  701. $id_in_path4 = $row3['id'];
  702. if ($_user['user_id'] == '') {
  703. $user_id = '0';
  704. } else {
  705. $user_id = $_user['user_id'];
  706. }
  707. // 4.b.3.2 Get user-item relation.
  708. $sql_items4 = "SELECT * FROM $tbl_learnpath_user WHERE c_id = $course_id AND (learnpath_item_id='$id_in_path4' and user_id=$user_id)";
  709. $result_items4 = Database::query($sql_items4);
  710. $row4 = Database::fetch_array($result_items4);
  711. // 4.b.3.3 If any of these elements is not 'completed', the overall completion status is false.
  712. if ($row4['status'] != 'completed' and $row4['status'] != 'passed') {
  713. $allcompleted = false;
  714. }
  715. }
  716. if ($allcompleted) {
  717. // 4.b.4.a All items were completed, prepare to return that there is no prerequisite blocking the way.
  718. $prereq = false;
  719. } else {
  720. // 4.b.4.b Something was not completed. Return corresponding message.
  721. $sql5 = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND (lp_id='$learnpath_id' and id='$id_in_path2')";
  722. $result5 = Database::query($sql5);
  723. $row5 = Database::fetch_array($result5);
  724. $prereqmodulename = trim($row5['chapter_name']);
  725. $prereq = $langPrereqModuleMinimum1.$prereqmodulename.$langPrereqModuleMinimum2;
  726. }
  727. } else {
  728. // 5. If prerequisite type undefined, no prereq.
  729. $prereq = false;
  730. }
  731. // 6. Return the message (or false if no prerequisite waiting).
  732. return ($prereq);
  733. }
  734. /**
  735. * Constructs the tree that will be used to build the learnpath structure
  736. * @params integer Learnpath_id
  737. * @return array Tree of the learnpath structure
  738. * @author Yannick Warnier <yannick.warnier@beeznest.com>
  739. * @comment This is a temporary function, which exists while the chapters and items
  740. * are still in separate tables in the database. This function gathers the data in a unique tree.
  741. */
  742. function get_learnpath_tree($learnpath_id) {
  743. //error_log('New LP - In learnpath_functions::get_learnpath_tree', 0);
  744. // Init elems
  745. //global $tbl_learnpath_item, $tbl_learnpath_chapter;
  746. /*
  747. $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
  748. $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
  749. */
  750. $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
  751. $tree = array();
  752. $chapters = array();
  753. $all_items_by_chapter = array();
  754. $course_id = api_get_course_int_id();
  755. $sql = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id = ".$learnpath_id." AND item_type='dokeos_chapter' ORDER BY display_order";
  756. //error_log('New LP - learnpath_functions - get_learnpath_tree: '.$sql,0);
  757. $res = Database::query($sql);
  758. // Format the $chapters_by_parent array so we have a suitable structure to work with.
  759. while ($row = Database::fetch_array($res)) {
  760. $chapters[] = $row;
  761. // Shouldn't be necessary (check no null value).
  762. if (empty ($row['parent_item_id'])) {
  763. $row['parent_item_id'] = 0;
  764. }
  765. //$chapters_by_parent[$row['parent_item_id']][$row['previous_item_id']] = $row;
  766. $all_items_by_chapter[$row['parent_item_id']][$row['display_order']] = $row;
  767. $all_items_by_chapter[$row['parent_item_id']][$row['display_order']]['type'] = 'dokeos_chapter';
  768. }
  769. // Now for every item in each chapter, get a suitable structure too.
  770. foreach ($chapters as $row) {
  771. // Select items from this chapter.
  772. $sql = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id = $learnpath_id AND parent_item_id = ".$row['id']." ORDER BY display_order";
  773. //error_log('New LP - learnpath_functions - get_learnpath_tree: '.$sql, 0);
  774. $res = Database::query($sql);
  775. //error_log('New LP - learnpath_functions - get_learnpath_tree: Found '.Database::num_rows($res).' results', 0);
  776. while ($myrow = Database::fetch_array($res, 'ASSOC')) {
  777. //$items[] = $myrow;
  778. //$items_by_chapter[$myrow['parent_item_id']][$myrow['display_order']] = $myrow;
  779. $all_items_by_chapter[$row['id']][$myrow['display_order']] = $myrow;
  780. $all_items_by_chapter[$row['id']][$myrow['display_order']]['type'] = 'item';
  781. }
  782. }
  783. //array_multisort($all_items_by_chapter[0], SORT_ASC, SORT_NUMERIC);
  784. foreach ($all_items_by_chapter as $key => $subrow) {
  785. ksort($all_items_by_chapter[$key]);
  786. }
  787. //all items should now be well-ordered
  788. //error_log('New LP - In get_learnpath_tree, returning '.print_r($all_items_by_chapter,true), 0);
  789. return $all_items_by_chapter;
  790. }
  791. /**
  792. * Gives a list of sequencial elements IDs for next/previous actions
  793. * @param array The elements tree as returned by get_learnpath_tree()
  794. * @param integer The chapter id to start from
  795. * @param boolean Whether to include chapters or not
  796. * @return array List of elements in the first to last order
  797. * @author Yannick Warnier <yannick.warnier@beeznest.com>
  798. */
  799. function get_ordered_items_list($tree, $chapter = 0, $include_chapters = false) {
  800. $list = array ();
  801. foreach ($tree[$chapter] as $order => $elem) {
  802. if ($elem['type'] == 'chapter') {
  803. if ($include_chapters === true) {
  804. $list[] = array ('id' => $elem['id'], 'type' => $elem['type']);
  805. }
  806. $res = get_ordered_items_list($tree, $elem['id'], $include_chapters);
  807. foreach ($res as $elem) {
  808. $list[] = $elem;
  809. }
  810. } elseif ($elem['type'] == 'item') {
  811. $list[] = array ('id' => $elem['id'], 'type' => $elem['type'], 'item_type' => $elem['item_type'], 'parent_item_id' => $elem['parent_item_id'], 'item_id' => $elem['item_id']);
  812. }
  813. }
  814. return $list;
  815. }
  816. /**
  817. * Displays the structure of a chapter recursively. Takes the result of get_learnpath_tree as argument
  818. * @param array Chapter structure
  819. * @param integer Chapter ID (start point in the tree)
  820. * @param integer Learnpath ID
  821. * @param integer User ID
  822. * @param boolean Indicates if the style is wrapped (true) or extended (false)
  823. * @param integer Level reached so far in the tree depth (enables recursive behaviour)
  824. * @return array Number of items, Number of items completed
  825. * @author Many changes by Yannick Warnier <yannick.warnier@beeznest.com>
  826. */
  827. function display_toc_chapter_contents($tree, $parent_item_id = 0, $learnpath_id, $uid, $wrap, $level = 0) {
  828. //global $tbl_learnpath_user;
  829. $tbl_learnpath_user = Database :: get_course_table(TABLE_LEARNPATH_USER);
  830. $num = 0;
  831. $num_completed = 0;
  832. foreach ($tree[$parent_item_id] as $order => $elem) {
  833. $bold = false;
  834. if (!empty ($_SESSION['cur_open']) && ($elem['id'] == $_SESSION['cur_open'])) {
  835. $bold = true;
  836. }
  837. if ($elem['type'] === 'chapter') {
  838. if ($wrap) {
  839. echo str_repeat("&nbsp;&nbsp;", $level).shorten(strip_tags($elem['chapter_name']), (35 - 3 * $level))."<br />\n";
  840. } else {
  841. echo "<tr><td colspan='3'>".str_repeat("&nbsp;&nbsp;", $level).shorten($elem['chapter_name'], (35 - 3 * $level))."</td></tr>\n";
  842. }
  843. if ($wrap) {
  844. if ($elem['chapter_description'] != '') {
  845. echo "<div class='description'>".str_repeat("&nbsp;&nbsp;", $level)."&nbsp;".shorten($elem['chapter_description'], (35 - 3 * $level))."</div>\n";
  846. }
  847. } else {
  848. if ($elem['chapter_description'] != '') {
  849. echo "<tr><td colspan='3'><div class='description'>".str_repeat("&nbsp;&nbsp;", $level)."&nbsp;".shorten($elem['chapter_description'], (35 - 3 * $level))."</div></td></tr>\n";
  850. }
  851. }
  852. list ($a, $b) = display_toc_chapter_contents($tree, $elem['id'], $learnpath_id, $uid, $wrap, $level + 1);
  853. $num += $a;
  854. $num_completed += $b;
  855. } elseif ($elem['type'] === 'item') {
  856. // If this element is an item (understand: not a directory/module).
  857. $sql0 = "SELECT * FROM $tbl_learnpath_user WHERE (user_id='".$uid."' and learnpath_item_id='".$elem['id']."' and lp_id='".$learnpath_id."')";
  858. $result0 = Database::query($sql0);
  859. $row0 = Database::fetch_array($result0);
  860. $completed = '';
  861. if (($row0['status'] == 'completed') or ($row0['status'] == 'passed')) {
  862. $completed = 'completed';
  863. $num_completed ++;
  864. }
  865. if ($wrap) {
  866. echo str_repeat("&nbsp;&nbsp;", $level)."<a name='{$elem['id']}' />\n";
  867. } else {
  868. echo "<tr><td>".str_repeat("&nbsp;&nbsp;", $level-1)."<a name='{$elem['id']}' />\n";
  869. }
  870. if ($wrap) {
  871. $icon = 'wrap';
  872. }
  873. if ($bold) {
  874. echo "<b>";
  875. }
  876. display_addedresource_link_in_learnpath($elem['item_type'], $elem['ref'], $completed, $elem['id'], 'player', $icon);
  877. if ($bold) {
  878. echo "</b>";
  879. }
  880. if ($wrap) {
  881. echo "<br />\n";
  882. } else {
  883. echo "</td></tr>\n";
  884. }
  885. $num ++;
  886. }
  887. }
  888. return array ($num, $num_completed);
  889. }
  890. /**
  891. * Returns a string to display in the tracking frame within the contents.php page (for example)
  892. * @param integer Learnpath id
  893. * @param integer Current user id
  894. * @param integer Starting chapter id
  895. * @param array Tree of elements as returned by get_learnpath_tree()
  896. * @param integer Level of recursivity we have reached
  897. * @param integer Counter of elements already displayed
  898. * @author Yannick Warnier <yannick.warnier@beeznest.com>
  899. * @deprecated this function seems to be unused
  900. * @note : forced display because of display_addedresource_link_in_learnpath behaviour (outputing a string would be better)
  901. */
  902. function get_tracking_table($learnpath_id, $user_id, $parent_item_id = 0, $tree = false, $level = 0, $counter = 0) {
  903. $tbl_learnpath_chapter = Database :: get_course_learnpath_chapter_table();
  904. $tbl_learnpath_item = Database :: get_course_learnpath_item_table();
  905. $tbl_learnpath_user = Database :: get_course_learnpath_user_table();
  906. //$mytable = '';
  907. $include_chapters = true;
  908. if (!is_array($tree)) {
  909. // Get a tree of the current learnpath elements.
  910. $tree = get_learnpath_tree($learnpath_id);
  911. }
  912. foreach ($tree[$parent_item_id] as $order => $elem) {
  913. if (($counter % 2) == 0) {
  914. $oddclass = 'row_odd';
  915. } else {
  916. $oddclass = 'row_even';
  917. }
  918. if ($elem['type'] == 'chapter') {
  919. if ($include_chapters === true) {
  920. //$mytable .= "<tr class='$oddclass'><td colspan = '3'>".str_repeat('&nbsp;',$level*2+2).$elem['chapter_name']."</td></tr>\n";
  921. echo "<tr class='$oddclass'><td colspan = '3'>".str_repeat('&nbsp;', $level * 2 + 2).$elem['chapter_name']."</td></tr>\n";
  922. }
  923. $counter ++;
  924. //$mytable .= get_tracking_table($learnpath_id, $user_id, $elem['id'], $tree, $level + 1, $counter );
  925. get_tracking_table($learnpath_id, $user_id, $elem['id'], $tree, $level + 1, $counter);
  926. } elseif ($elem['type'] == 'item') {
  927. $sql = "SELECT * FROM $tbl_learnpath_user "."WHERE user_id = $user_id "."AND lp_id = $learnpath_id "."AND learnpath_item_id = ".$elem['id'];
  928. $res = Database::query($sql);
  929. $myrow = Database::fetch_array($res);
  930. if (($myrow['status'] == 'completed') || ($myrow['status'] == 'passed')) {
  931. $color = 'blue';
  932. $statusmessage = get_lang('Complete');
  933. } else {
  934. $color = 'black';
  935. $statusmessage = get_lang('Incomplete');
  936. }
  937. $link = get_addedresource_link_in_learnpath($elem['item_type'], $elem['id'], $elem['item_id']);
  938. //$link = display_addedresource_link_in_learnpath($elem['item_type'], $elem['id'], $row['status'], $elem['item_id'], 'player', 'none');
  939. //$mytable .= "<tr class='$oddclass'>"
  940. echo "<tr class='$oddclass'>"."<td class='mystatus'>".str_repeat("&nbsp;", $level * 2 + 2);
  941. //."<a href='$link?SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9' target='toc'>hop</a>"
  942. display_addedresource_link_in_learnpath($elem['item_type'], $elem['ref'], $myrow['status'], $elem['id'], 'player', 'wrap');
  943. //we should also add the total score here
  944. echo "<td>"."<font color='$color'><div class='mystatus'>".$statusmessage."</div></font>"."</td>"."<td>"."<div class='mystatus' align='center'>". ($myrow['score'] == 0 ? '-' : $myrow['score'])."</div>"."</td>"."</tr>\n";
  945. $counter ++;
  946. }
  947. }
  948. //return $mytable;
  949. return true;
  950. }
  951. /**
  952. * This function returns false if there is at least one item in the path
  953. * @param Learnpath ID
  954. * @return boolean True if nothing was found, false otherwise
  955. */
  956. function is_empty($id) {
  957. $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
  958. $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
  959. $course_id = api_get_course_int_id();
  960. $sql = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND lp_id=$id ORDER BY display_order ASC";
  961. $result = Database::query($sql);
  962. $num_modules = Database::num_rows($result);
  963. $empty = true;
  964. if ($num_modules != 0) {
  965. while ($row = Database::fetch_array($result)) {
  966. $num_items = 0;
  967. $parent_item_id = $row['id'];
  968. $sql2 = "SELECT * FROM $tbl_learnpath_item WHERE c_id = $course_id AND (parent_item_id=$parent_item_id) ORDER BY display_order ASC";
  969. $result2 = Database::query($sql2);
  970. $num_items = Database::num_rows($result2);
  971. if ($num_items > 0) {
  972. $empty = false;
  973. }
  974. }
  975. }
  976. return ($empty);
  977. }
  978. /**
  979. * This function writes $content to $filename
  980. * @param string Destination filename
  981. * @param string Learnpath name
  982. * @param integer Learnpath ID
  983. * @param string Content to write
  984. * @return void
  985. */
  986. function exporttofile($filename, $LPname, $LPid, $content) {
  987. global $circle1_files; // This keeps all the files which are exported [0]:filename [1]:LP name.
  988. // The $circle1_files variable is going to be used to a deep extent in the imsmanifest.xml.
  989. global $expdir;
  990. if (!$handle = fopen($expdir.'/'.$filename, 'w')) {
  991. echo "Cannot open file ($filename)";
  992. }
  993. if (fwrite($handle, $content) === false) {
  994. echo "Cannot write to file ($filename)";
  995. exit;
  996. }
  997. fclose($handle);
  998. $circle1_files[0][] = $filename;
  999. $circle1_files[1][] = $LPname;
  1000. $circle1_files[2][] = $LPid;
  1001. }
  1002. /**
  1003. * This function exports the given Chamilo test
  1004. * @param integer Test ID
  1005. * @return string The test itself as an HTML string
  1006. */
  1007. function export_exercise($item_id) {
  1008. global $expdir, $_course, $_configuration, $_SESSION, $_SERVER, $language_interface, $langExerciseNotFound, $langQuestion, $langOk, $origin, $questionNum;
  1009. $exerciseId = $item_id;
  1010. require_once '../exercice/testcategory.class.php';
  1011. require_once '../exercice/exercise.class.php';
  1012. require_once '../exercice/question.class.php';
  1013. require_once '../exercice/answer.class.php';
  1014. require_once '../exercice/exercise.lib.php';
  1015. $TBL_EXERCISES = Database :: get_course_table(TABLE_QUIZ_TEST);
  1016. /* Clears the exercise session */
  1017. if (isset ($_SESSION['objExercise'])) {
  1018. Session::erase('objExercise');
  1019. }
  1020. if (isset ($_SESSION['objQuestion'])) {
  1021. Session::erase('objQuestion');
  1022. }
  1023. if (isset ($_SESSION['objAnswer'])) {
  1024. Session::erase('objAnswer');
  1025. }
  1026. if (isset ($_SESSION['questionList'])) {
  1027. Session::erase('questionList');
  1028. }
  1029. if (isset ($_SESSION['exerciseResult'])) {
  1030. Session::erase('exerciseResult');
  1031. }
  1032. // If the object is not in the session:
  1033. if (!isset ($_SESSION['objExercise'])) {
  1034. // Construction of Exercise.
  1035. $objExercise = new Exercise();
  1036. $sql = "SELECT title,description,sound,type,random,active FROM $TBL_EXERCISES WHERE id='$exerciseId'";
  1037. // If the specified exercise doesn't exist or is disabled:
  1038. if (!$objExercise->read($exerciseId) || (!$objExercise->selectStatus() && !api_is_allowed_to_edit() && ($origin != 'learnpath'))) {
  1039. die($langExerciseNotFound);
  1040. }
  1041. // Saves the object into the session.
  1042. Session::write('objExercise',$objExercise);
  1043. }
  1044. $exerciseTitle = $objExercise->selectTitle();
  1045. $exerciseDescription = $objExercise->selectDescription();
  1046. $exerciseSound = $objExercise->selectSound();
  1047. $randomQuestions = $objExercise->isRandom();
  1048. $exerciseType = $objExercise->selectType();
  1049. if (!isset ($_SESSION['questionList'])) {
  1050. // Selects the list of question ID.
  1051. $questionList = $randomQuestions ? $objExercise->selectRandomList() : $objExercise->selectQuestionList();
  1052. // Saves the question list into the session.
  1053. Session::write('questionList',$questionList);
  1054. }
  1055. $nbrQuestions = sizeof($questionList);
  1056. // If questionNum comes from POST and not from GET:
  1057. if (!$questionNum || $_POST['questionNum']) {
  1058. // Only used for sequential exercises (see $exerciseType).
  1059. if (!$questionNum) {
  1060. $questionNum = 1;
  1061. } else {
  1062. $questionNum ++;
  1063. }
  1064. }
  1065. $exerciseTitle = text_filter($exerciseTitle);
  1066. $test .= "<h3>".$exerciseTitle."</h3>";
  1067. if (!empty ($exerciseSound)) {
  1068. $test .= "<a href=\"../document/download.php?doc_url=%2Faudio%2F".$exerciseSound."\"&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9 target=\"_blank\"><img src=\"../img/sound.gif\" border=\"0\" align=\"absmiddle\" alt=".get_lang("Sound")."\" /></a>";
  1069. }
  1070. $exerciseDescription = text_filter($exerciseDescription);
  1071. // Writing the .js file with to check the correct answers begin.
  1072. $scriptfilename = "Exercice".$item_id.".js";
  1073. $s = "<script type=\"text/javascript\" src='../js/".$scriptfilename."'></script>";
  1074. $test .= $s;
  1075. $content = "function evaluate() {
  1076. alert('Test evaluated.');
  1077. }
  1078. ";
  1079. if (!$handle = fopen($expdir.'/js/'.$scriptfilename, 'w')) {
  1080. echo "Cannot open file ($scriptfilename)";
  1081. }
  1082. if (fwrite($handle, $content) === false) {
  1083. echo "Cannot write to file ($filename)";
  1084. exit;
  1085. }
  1086. fclose($handle);
  1087. // Writing the .js file with to check the correct answers end.
  1088. $s = "
  1089. <p>$exerciseDescription</p>
  1090. <table width='100%' border='0' cellpadding='1' cellspacing='0'>
  1091. <form method='post' action=''><input type=\"hidden\" name=\"SQMSESSID\" value=\"36812c2dea7d8d6e708d5e6a2f09b0b9\" />
  1092. <input type='hidden' name='formSent' value='1' />
  1093. <input type='hidden' name='exerciseType' value='".$exerciseType."' />
  1094. <input type='hidden' name='questionNum' value='".$questionNum."' />
  1095. <input type='hidden' name='nbrQuestions' value='".$nbrQuestions."' />
  1096. <tr>
  1097. <td>
  1098. <table width='100%' cellpadding='4' cellspacing='2' border='0'>";
  1099. $test .= $s;
  1100. $i = 0;
  1101. foreach ($questionList as $questionId) {
  1102. $i ++;
  1103. echo $s = "<tr bgcolor='#e6e6e6'><td valign='top' colspan='2'>".get_lang('Question')." ";
  1104. // Call the showQuestion() function from exercise.lib.php. This basically displays the question in a table.
  1105. $test .= showQuestion($questionId, false, 'export', $i);
  1106. } // end foreach()
  1107. $s = "</table></td></tr><tr><td><br/><input type='button' value='".$langOk."' onclick=\"javascript: evaluate(); alert('Evaluated.');\">";
  1108. $s .= "</td></tr></form></table>";
  1109. $s .= "<script type='text/javascript'> loadPage(); </script>";
  1110. $b = 2;
  1111. $test .= $s;
  1112. return ($test);
  1113. }
  1114. /**
  1115. * This function exports the given item
  1116. * @param integer Id from learnpath_items table
  1117. * @param integer Item id
  1118. * @param string Itm type
  1119. * @param boolean Shall the SCORM communications features be added? (true). Default: false.
  1120. * @return void (outputs a zip file)
  1121. * @todo Try using the SCORM communications addition (adding a button and several javascript calls to the SCORM API) elsewhere than just in the export feature, so it doesn't look like an incoherent feature
  1122. */
  1123. function exportitem($id, $item_id, $item_type, $add_scorm_communications = false) {
  1124. $course_id = api_get_course_int_id();
  1125. global $circle1_files, $expdir, $_course, $_SESSION, $GLOBALS;
  1126. global $timeNoSecFormat, $dateFormatLong, $language_interface, $langPubl, $langDone, $langThisCourseDescriptionIsEmpty, $lg_course_description, $lg_introduction_text, $_cid, $langHotPotatoesFinished, $lg_author, $lg_date, $lg_groups, $lg_users, $lg_ass, $lg_dropbox, $test, $langQuestion;
  1127. $libp = api_get_path(SYS_CODE_PAH);
  1128. include_once $libp.'exercice/exercise.class.php';
  1129. include_once $libp.'question.class.php';
  1130. include_once $libp.'answer.class.php';
  1131. include_once $libp.'exercise.lib.php';
  1132. $langLasting = '';//avoid code parser warning
  1133. include_once $libp.'lang/english/announcements.inc.php'; //this line is here only for $langPubl in announcements
  1134. include_once $libp.'lang/'.$language_interface.'/announcements.inc.php'; //this line is here only for $langPubl in announcements
  1135. include_once $libp.'lang/english/agenda.inc.php'; //this line is here only for $langLasting
  1136. include_once $libp.'lang/'.$language_interface.'/agenda.inc.php'; //this line is here only for $langLasting
  1137. include_once $libp.'lang/english/course_description.inc.php'; //this line is here only for $langThisCourseDescriptionIsEmpty
  1138. include_once $libp.'lang/'.$language_interface.'/course_description.inc.php'; // -||-
  1139. include_once $libp.'lang/english/resourcelinker.inc.php';
  1140. include_once $libp.'lang/'.$language_interface.'/resourcelinker.inc.php';
  1141. include_once $libp.'lang/english/learnpath.inc.php';
  1142. include_once $libp.'lang/'.$language_interface.'/learnpath.inc.php';
  1143. include_once $libp.'lang/english/exercice.inc.php';
  1144. include_once $libp.'lang/'.$language_interface.'/exercice.inc.php';
  1145. include_once '../resourcelinker/resourcelinker.inc.php';
  1146. $LPname = display_addedresource_link_in_learnpath($item_type, $item_id, '', $id, 'builder', 'nolink');
  1147. $expcontent = "<!--
  1148. This is an exported file from Chamilo Learning Path belonging to a Scorm compliant content package.
  1149. Do not modify or replace individually.
  1150. Export module author : Denes Nagy <darkden@evk.bke.hu>
  1151. -->
  1152. ";
  1153. // Files needed for communicating with the scos.
  1154. $scocomfiles = "<script type='text/javascript' src='../js/APIWrapper.js'></script>"."<script type='text/javascript' src='../js/SCOFunctions.js'></script>";
  1155. $expcontent .= '<html><head><link rel="stylesheet" href="../css/default.css" type="text/css" media="screen,projection" />'.$scocomfiles.'</head><body>';
  1156. $donebutton .= "<script type='text/javascript'>
  1157. /* <![CDATA[ */
  1158. loadPage();
  1159. var studentName = '!';
  1160. var lmsStudentName = doLMSGetValue( 'cmi.core.student_name' );
  1161. if ( lmsStudentName != '' )
  1162. {
  1163. studentName = ' ' + lmsStudentName + '!';
  1164. }
  1165. /* ]]> */
  1166. </script>
  1167. <br /><br />
  1168. <form><input type=\"hidden\" name=\"SQMSESSID\" value=\"36812c2dea7d8d6e708d5e6a2f09b0b9\" />
  1169. <table cols='3' width='100%' align='center'>
  1170. <tr>
  1171. <td align='middle'><input type = 'button' value = ' ".$langDone." ' onclick = \"javascript: doQuit('completed');\" id='button2' name='button2'></td>
  1172. </tr>
  1173. </table>
  1174. </form>";
  1175. /**
  1176. * Switch between the different element types, namely:
  1177. * - Agenda
  1178. * - Ad_Valvas
  1179. * - Course_description
  1180. * - Document
  1181. * - Introduction_text
  1182. * - HotPotatoes
  1183. * - Exercise
  1184. * - Post
  1185. * - Forum ]
  1186. * - Thread ]
  1187. * - Dropbox ]
  1188. * - Assignments ] These elements are all replaced by a simple message in the exported document.
  1189. * - Groups ]
  1190. * - Users ]
  1191. * - Link _self
  1192. * - Link _blank
  1193. */
  1194. switch ($item_type) {
  1195. // AGENDA BEGIN
  1196. case 'Agenda':
  1197. // 1. Get agenda event data from the database table.
  1198. $TABLEAGENDA = Database :: get_course_table(TABLE_AGENDA);
  1199. $sql = "SELECT * FROM ".$TABLEAGENDA." where c_id = $course_id AND (id=$item_id)";
  1200. $result = Database::query($sql);
  1201. // 2. Prepare table output.
  1202. $expcontent .= "<table class=\"data_table\" >";
  1203. $barreMois = '';
  1204. // 3. For each event corresponding to this agenda, do the following:
  1205. while ($myrow = Database::fetch_array($result)) {
  1206. $start_date_local = api_get_local_time($myrow['start_date'], null, date_default_timezone_get());
  1207. //3.1 Make the blue month bar appear only once.
  1208. if ($barreMois != api_format_date($start_date_local, "%m")) {
  1209. // 3.1.1 Update the check value for the month bar.
  1210. $barreMois = api_format_date($start_date_local, "%m");
  1211. // 3.1.2 Display the month bar.
  1212. $expcontent .= "<tr><td id=\"title\" colspan=\"2\" class=\"month\" valign=\"top\">".api_format_date($start_date_local, "%B %Y")."</td></tr>";
  1213. }
  1214. // 3.2 Display the agenda items (of this month): the date, hour and title.
  1215. $db_date = (int) api_format_date($start_date_local, "%d");
  1216. if ($_GET['day'] != $db_date) {
  1217. // 3.2.1.a If the day given in the URL (might not be set) is different from this element's day, use style 'data'.
  1218. $expcontent .= "<tr><td class=\"data\" colspan='2'>";
  1219. } else {
  1220. // 3.2.1.b Else (same day) use style 'datanow'.
  1221. $expcontent .= "<tr><td class=\"datanow\" colspan='2'>";
  1222. }
  1223. // 3.2.2 Mark an anchor for this date.
  1224. $expcontent .= "<a name=\"".$db_date."\"></a>"; // anchoring
  1225. // 3.2.3 Write the date and time of this event to the export string.
  1226. $expcontent .= api_format_date($start_date_local);
  1227. // 3.2.4 If a duration is set, write it, otherwise ignore.
  1228. if ($myrow['duration'] == '') {
  1229. $expcontent .= "<br />";
  1230. } else {
  1231. $expcontent .= " / ".$langLasting." ".$myrow['duration']."<br />"; //langLasting comes from lang/x/agenda.inc.php
  1232. }
  1233. // 3.2.5 Write the title.
  1234. $expcontent .= $myrow['title'];
  1235. $expcontent .= "</td></tr>";
  1236. // 3.2.6 Prepare the content of the agenda item.
  1237. $content = $myrow['content'];
  1238. // 3.2.7 Make clickable???
  1239. $content = make_clickable($content);
  1240. $content = text_filter($content);
  1241. // 3.2.8 Write the prepared content to the export string.
  1242. $expcontent .= "<tr><td class=\"text\" colspan='2'>";
  1243. $expcontent .= $content;
  1244. $expcontent .= "</td></tr>";
  1245. // Displaying the agenda item of this month: the added resources.
  1246. // This part is not included into LP export.
  1247. /*if (check_added_resources("Agenda", $myrow['id'])) {
  1248. $content.= "<tr><td colspan='2'>";
  1249. $content.= "<i>".get_lang('AddedResources')."</i><br />";
  1250. display_added_resources("Agenda", $myrow['id']);
  1251. $content.= "</td></tr>";
  1252. }*/
  1253. }
  1254. // 4. Finish the export string.
  1255. $expcontent .= "<tr></table>";
  1256. break;
  1257. // ANNOUNCEMENT BEGIN
  1258. case 'Ad_Valvas':
  1259. // 1. Get the announcement data from the database
  1260. $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
  1261. $sql = "SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id='$item_id'";
  1262. $result = Database::query($sql);
  1263. // 2. Initialise export string
  1264. $expcontent .= "<table class=\"data_table\">";
  1265. // 3. For each announcement matching the query
  1266. while ($myrow = Database::fetch_array($result)) {
  1267. // 3.1 Get the __ field data.
  1268. $content = $myrow[1];
  1269. //$content = nl2br($content);
  1270. // 3.2 Prepare the data for export.
  1271. $content = make_clickable($content);
  1272. $content = text_filter($content);
  1273. // 3.3 Get a UNIX(?<-mktime) Timestamp of the end_date for this announcement.
  1274. $last_post_datetime = $myrow['end_date']; // post time format datetime of database layer (MySQL is assumed)
  1275. list ($last_post_date, $last_post_time) = split(' ', $last_post_datetime);
  1276. list ($year, $month, $day) = explode('-', $last_post_date);
  1277. list ($hour, $min) = explode(':', $last_post_time);
  1278. $announceDate = mktime($hour, $min, 0, $month, $day, $year);
  1279. // 3.4 Compare the end date to the last login date of the user (mark it in red if he has not already read it).
  1280. if ($announceDate > $_SESSION['user_last_login_datetime']) {
  1281. $colorBecauseNew = " color=\"red\" ";
  1282. } else {
  1283. $colorBecauseNew = ' ';
  1284. }
  1285. // 3.5 Write this content to the export string (formatted HTML array).
  1286. $expcontent .= "<tr>\n"."<td class=\"cell_header\">\n"."<font ".$colorBecauseNew.">".$langPubl." : ".api_convert_and_format_date($last_post_datetime, null, date_default_timezone_get())."</font>\n"."</td>\n"."</tr>\n"."<tr>\n"."<td>\n".$content."</td>\n"."</tr>\n";
  1287. } // while loop
  1288. // 4 Finish the export string.
  1289. $expcontent .= "</table>";
  1290. break;
  1291. // Course_description BEGIN
  1292. case 'Course_description':
  1293. // 1. Get course description data from database.
  1294. $tbl_course_description = Database :: get_course_table(TABLE_COURSE_DESCRIPTION);
  1295. $result = Database::query("SELECT id, title, content FROM ".$tbl_course_description." WHERE c_id = $course_id ORDER BY id");
  1296. // 2. Check this element.
  1297. if (Database::num_rows($result)) {
  1298. // 2.a This course has one (or more) description in the database.
  1299. $expcontent .= "<hr noshade=\"noshade\" size=\"1\" />";
  1300. // 2.a.1 For each description available for this course.
  1301. while ($row = Database::fetch_array($result)) {
  1302. // 2.a.1.1 Write title to export string.
  1303. $expcontent .= "<h4>".$row['title']."</h4>";
  1304. // 2.a.1.2 Prepare content.
  1305. $content = make_clickable(nl2br($row['content']));
  1306. $content = text_filter($content);
  1307. // 2.a.1.3 Write content to the export string.
  1308. $expcontent .= $content;
  1309. }
  1310. } else {
  1311. // 2.b This course has no description available.
  1312. $expcontent .= "<br /><h4>$langThisCourseDescriptionIsEmpty</h4>";
  1313. }
  1314. break;
  1315. // DOCUMENT BEGIN
  1316. case 'Document':
  1317. // 1. Get the document data from the database.
  1318. $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
  1319. $sql_query = "SELECT * FROM $tbl_document WHERE c_id = $course_id AND id=$item_id";
  1320. $sql_result = Database::query($sql_query);
  1321. $myrow = Database::fetch_array($sql_result);
  1322. // 2. Get the origin path of the document to treat it internally.
  1323. $orig = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$myrow['path'];
  1324. // 3. Make some kind of strange transformation to get the destination filepath ???
  1325. $pathname = explode('/', $myrow['path']);
  1326. $last = count($pathname) - 1;
  1327. $filename = 'data/'.$filename.$pathname[$last];
  1328. $copyneeded = true;
  1329. // Html files do not need to be copied as the ok button is inserted into them,
  1330. // so don't copy directly.
  1331. $extension = explode('.', $pathname[$last]);
  1332. // This old condition was WRONG for names like design.html.old. Instead, we now get the extension
  1333. // by using preg_match to match case-insensitive (probably faster than 4 conditions).
  1334. //if (($extension[1]=='htm') or ($extension[1]=='html') or ($extension[1]=='HTM') or ($extension[1]=='HTML')) {
  1335. // 4. Check the file extension.
  1336. if (preg_match('/.*(\.htm(l)?)$/i', $pathname[$last])) {
  1337. // 4.a If this file ends with ".htm(l)", we consider it's an HTML file.
  1338. // src tag check begin
  1339. // We now check if there is any src attribute in htm(l) files, if yes, we have to also export
  1340. // the target file (swf, mp3, video,...) of that src tag.
  1341. // In case of absolute links (http://) this is not neccessary, but makes no error.
  1342. // However still missing : relative links case with subdirs -> the necessary dirs are not created in the exported package.
  1343. // 4.a.1 Get the file contents into $file.
  1344. $file = file_get_contents($orig);
  1345. // 4.a.2 Get all the src links in this file.
  1346. //preg_match_all("|((?i)src=\".*\" )|U",$file,$match);
  1347. $match = GetSRCTags($orig);
  1348. // 4.a.3 For each src tag found, do the following:
  1349. for ($i = 0; $i < count($match); $i ++) {
  1350. // 4.a.3.1 Get the tag (split from the key).
  1351. list ($key, $srctag) = each($match);
  1352. $src = $srctag;
  1353. // 4.a.3.2 Check the link kind (web or absolute/relative).
  1354. if (stristr($src, 'http') === false) {
  1355. // 4.a.3.2.a Do something only if relative (otherwise the user will be able to see it too anyway).
  1356. // 4.a.3.2.a.1 Get a proper URL and remove all './'
  1357. $src = urldecode($src); //mp3
  1358. //$src=str_replace('./','',$src);
  1359. $src = preg_replace('/^\.\//', '', $src);
  1360. // 4.a.3.2.a.2 Remove the player link from the URL (only use the mp3 file).
  1361. $src = str_replace('mp3player.swf?son=', '', $src); //mp3
  1362. // 4.a.3.2.a.3 Remove funny link parts.
  1363. $src = str_replace('?0', '', $src); //mp3
  1364. // The previous lines are used when creating docs with Chamilo Document tool's htmlarea
  1365. // Rows marked by 'mp3' are needed because the mp3 plugin inserts the swf-mp3 links in a very strange way
  1366. // and we can decode them with those 3 lines, hoping this will not cause errors in case of other htmls,
  1367. // created by any other software.
  1368. // 4.a.3.2.a.4 Prepare source and destination paths.
  1369. $source = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.dirname($myrow['path']).'/'.$src;
  1370. $dest = $expdir.'/data/'.$src;
  1371. //CopyNCreate($source,$dest);
  1372. rcopy($source, $dest);
  1373. } //else...?
  1374. }
  1375. // src tag check end
  1376. // sco communication insertion begin
  1377. // 4.a.4 If we want to add SCORM actions and a "Done" button, do the following:
  1378. if ($add_scorm_communications === true) {
  1379. if ($bodyclose = strpos($file, '</body>')) {
  1380. $file = substr_replace($file, $scocomfiles.$donebutton, $bodyclose, 7);
  1381. } elseif ($htmlclose = strpos($file, '</html>')) {
  1382. $file = substr_replace($file, $scocomfiles.$donebutton, $htmlclose, 7);
  1383. $file .= '</html>';
  1384. } else {
  1385. $file .= $scocomfiles.$donebutton;
  1386. }
  1387. } //sco communication insertion end
  1388. // 4.a.5 Replace the file's name by adding the element's ID before htm.
  1389. // This will not work with uppercase HTML though. Maybe use the preg_replace syntax proposed...
  1390. $filename = str_replace('.htm', $id.'.htm', $filename);
  1391. //$filename=preg_replace('/.*(\.htm(l)?)$/i',$id.$1,$filename);
  1392. // 4.a.6 Export these contents to a file and set the circle1_files array for later reuse.
  1393. exporttofile($filename, $LPname, $id, $file);
  1394. // The file has been copied, so ask not to copy it again.
  1395. $copyneeded = false;
  1396. } //if (htm(l) files) end
  1397. // 5. If we still need to copy the file (e.g. it was not an HTML file), then copy and set circle1_files for later reuse.
  1398. if ($copyneeded) {
  1399. copy($orig, $expdir.'/'.$filename);
  1400. $circle1_files[0][] = $filename;
  1401. $circle1_files[1][] = $LPname;
  1402. $circle1_files[2][] = $id;
  1403. }
  1404. //echo $orig;
  1405. return;
  1406. // Introduction_text BEGIN
  1407. case 'Introduction_text':
  1408. // 1 Get the introduction text data from the database.
  1409. $TBL_INTRO = Database :: get_course_tool_intro_table();
  1410. // Modified by Ivan Tcholakov, 15-SEP-2008.
  1411. //$result = Database::query("SELECT * FROM ".$TBL_INTRO." WHERE id=1");
  1412. $result = Database::query("SELECT * FROM ".$TBL_INTRO." WHERE c_id = $course_id AND id='course_homepage'");
  1413. //
  1414. $myrow = Database::fetch_array($result);
  1415. $intro = $myrow['intro_text'];
  1416. // 2 Write introduction text to the export string.
  1417. $expcontent .= '<br />'.$intro;
  1418. break;
  1419. // HotPotatoes BEGIN
  1420. case 'HotPotatoes':
  1421. // 1. Get HotPotatoes data from the document table.
  1422. $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
  1423. $result = Database::query("SELECT * FROM $tbl_document WHERE c_id = $course_id AND id=$item_id");
  1424. $myrow = Database::fetch_array($result);
  1425. // 2. Get the document path.
  1426. $testfile = api_get_path(SYS_COURSE_PATH).$_course['path']."/document".urldecode($myrow['path']);
  1427. // 3. Get the document contents into a string.
  1428. $content = file_get_contents($testfile);
  1429. // 4. Get the document filename (just the file, no path) - would probably be better to use PHP native function.
  1430. $pathname = explode('/', $myrow['path']);
  1431. $last = count($pathname) - 1;
  1432. $filename = 'data/'.$filename.$pathname[$last];
  1433. // 4beta - get all linked files and copy them (procedure copied from documents type).
  1434. // Get all the src links in this file.
  1435. $match = GetSRCTags($testfile);
  1436. // For each src tag found, do the following:
  1437. foreach ($match as $src) {
  1438. //Check the link kind (web or absolute/relative)
  1439. if (stristr($src, 'http') === false) {
  1440. // Do something only if relative (otherwise the user will be able to see it too anyway).
  1441. // Get a proper URL and remove all './'
  1442. $src = urldecode($src); //mp3
  1443. $src = str_replace('./', '', $src);
  1444. // Remove the player link from the URL (only use the mp3 file).
  1445. $src = str_replace('mp3player.swf?son=', '', $src); //mp3
  1446. // Remove funny link parts.
  1447. $src = str_replace('?0', '', $src); //mp3
  1448. // The previous lines are used when creating docs with Chamilo Document tool's htmlarea.
  1449. // Rows marked by 'mp3' are needed because the mp3 plugin inserts the swf-mp3 links in a very strange way
  1450. // and we can decode them with those 3 lines, hoping this will not cause errors in case of other htmls,
  1451. // created by any other software.
  1452. // Prepare source and destination paths.
  1453. $source = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.dirname($myrow['path']).'/'.$src;
  1454. $dest = $expdir.'/data/'.$src;
  1455. //CopyNCreate($source,$dest);
  1456. rcopy($source, $dest);
  1457. } //else...?
  1458. }
  1459. // 5. Prepare the special "close window" for this test.
  1460. $closewindow = "<html><head><link rel='stylesheet' type='text/css' href='../css/default.css'></head><body>"."<br /><div class='message'>$langHotPotatoesFinished</div></body></html>";
  1461. // Finish is the function of HP to save scores, we insert our scorm function calls to its beginning
  1462. // 'Score' is the variable that tracks the score in HP tests.
  1463. // 6.
  1464. $mit = "function Finish(){";
  1465. $js_content = "var SaveScoreVariable = 0; // This variable is included by Chamilo LP export\n"."function mySaveScore() // This function is included by Chamilo LP export\n"."{\n"." if (SaveScoreVariable==0)\n"." {\n"." SaveScoreVariable = 1;\n".
  1466. //the following function are implemented in SCOFunctions.js
  1467. " exitPageStatus = true;\n"." computeTime();\n"." doLMSSetValue( 'cmi.core.score.raw', Score );\n"." doLMSSetValue( 'cmi.core.lesson_status', 'completed' );\n"." doLMSCommit();\n"." doLMSFinish();\n".
  1468. // " document.write('".$closewindow."');\n".
  1469. //if you insert the previous row, the test does not appear correctly !!!!
  1470. " }\n"."}\n"."function Finish(){\n"." mySaveScore();";
  1471. $start = "<script type='text/javascript'> loadPage(); </script>";
  1472. // 7. Replace the current MIT function call by our set of functions. In clear, transform HP to SCORM.
  1473. $content = str_replace($mit, $js_content, $content);
  1474. // 8. Finally, add the API loading calls (although that might have been done first).
  1475. $content = str_replace("</script>", "</script>".$scocomfiles.$start, $content);
  1476. // 9. Change the filename to add the database ID and export to a new file,
  1477. // setting the circle1_files array for later reuse.
  1478. $filename = str_replace('.htm', $id.'.htm', $filename);
  1479. exporttofile($filename, $LPname, $id, $content);
  1480. return;
  1481. // Chamilo test BEGIN
  1482. case 'Exercise':
  1483. //1 Use the export_exercise() function to do the job of constructing the question's HTML table
  1484. $expcontent .= export_exercise($item_id);
  1485. break;
  1486. // POST BEGIN
  1487. case 'Post':
  1488. // 1. Get the forum post data from the database.
  1489. $tbl_posts =Database::get_course_table(TABLE_FORUM_POST);
  1490. $tbl_posts_text =Database::get_course_table(TOOL_FORUM_POST_TEXT_TABLE);
  1491. $result = Database::query("SELECT * FROM $tbl_posts where c_id = $course_id AND post_id=$item_id");
  1492. $myrow = Database::fetch_array($result);
  1493. // Grabbing the title of the post.
  1494. $sql_titel = "SELECT * FROM $tbl_posts_text WHERE c_id = $course_id AND post_id=".$myrow['post_id'];
  1495. $result_titel = Database::query($sql_titel);
  1496. $myrow_titel = Database::fetch_array($result_titel);
  1497. $posternom = $myrow['nom'];
  1498. $posterprenom = $myrow['prenom'];
  1499. $posttime = $myrow['post_time'];
  1500. $posttext = $myrow_titel['post_text'];
  1501. $posttitle = $myrow_titel['post_title'];
  1502. $posttext = str_replace('"', "'", $posttext);
  1503. //2 Export contents as an HTML table
  1504. $expcontent .= "<table border='0' cellpadding='3' cellspacing='1' width='100%'>
  1505. <tr>
  1506. <td colspan='2' bgcolor='#e6e6e6'><b>$posttitle</b><br />$posttext</td>
  1507. </tr>
  1508. <tr>
  1509. <td colspan='2'></td>
  1510. </tr>
  1511. <tr>
  1512. <td bgcolor='#cccccc' align='left'>$lg_author : $posterprenom $posternom</td>
  1513. <td align='right' bgcolor='#cccccc'>$lg_date : $posttime</td>
  1514. </tr>
  1515. <tr><td colspan='2' height='10'></td></tr>
  1516. </table>";
  1517. break;
  1518. // NOT IMPLEMENTED ITEMS BEGIN
  1519. case 'Forum':
  1520. case 'Thread':
  1521. case 'Dropbox':
  1522. case 'Assignments':
  1523. case 'Groups':
  1524. case 'Users':
  1525. // 1. Instead of building something, put an info message.
  1526. $langItemMissing1 = "There was a ";
  1527. $langItemMissing2 = "page (step) here in the original Chamilo Learning Path.";
  1528. $expcontent .= "<div class='message'>$langItemMissing1 $item_type $langItemMissing2</div>";
  1529. break;
  1530. // Link BEGIN
  1531. case 'Link _self':
  1532. case 'Link _blank':
  1533. // 1. Get the link data from the database.
  1534. $TABLETOOLLINK = Database :: get_course_link_table();
  1535. $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$item_id");
  1536. $myrow = Database::fetch_array($result);
  1537. $thelink = $myrow['url'];
  1538. // 2. Check the link type (open in blank page or in current page).
  1539. if ($item_type == 'Link _blank')
  1540. {
  1541. $target = '_blank';
  1542. }
  1543. // 3. Write the link to the export string.
  1544. $expcontent .= "<a href='$thelink?SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9' target='".$target."'>$LPname</a>";
  1545. // 4. Change the element type for later changes (this is lost, however, so useless here).
  1546. $item_type = 'Link'; // To put this to the filename.
  1547. //$LPname="<a href='$thelink?SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9' target=".$target.">$LPname</a>";
  1548. // I am still not sure about Link export : to export them as files or they can appear in the TOC at once ?
  1549. // To enable the second possibility, unrem the row $LPname=...
  1550. break;
  1551. }
  1552. // Now we add the Done button and the initialize function : loadpage()
  1553. // not in the case of Documents, HotP
  1554. if ($item_type != 'Exercise' and ($add_scorm_communications === true)) {
  1555. $expcontent .= $donebutton;
  1556. }
  1557. // End the export string with valid HTML tags.
  1558. $expcontent .= "</body></html>";
  1559. // Prepare new file name.
  1560. $filename = $item_type.$id.".htm";
  1561. // Write the export content to the new file.
  1562. exporttofile('data/'.$filename, $LPname, $id, $expcontent);
  1563. }
  1564. /**
  1565. * This function exports the given item's description into a separate file
  1566. * @param integer Item id
  1567. * @param string Item type
  1568. * @param string Description
  1569. * @return void
  1570. */
  1571. function exportdescription($id, $item_type, $description) {
  1572. global $expdir;
  1573. $filename = $item_type.$id.'.desc';
  1574. $expcontent = $description;
  1575. exporttofile($expdir.$filename, 'description_of_'.$item_type.$id, 'description_of_item_'.$id, $expcontent);
  1576. }
  1577. /**
  1578. * This function deletes an entire directory
  1579. * @param string The directory path
  1580. * @return boolean True on success, false on failure
  1581. */
  1582. function deldir($dir) {
  1583. $dh = opendir($dir);
  1584. while ($file = readdir($dh)) {
  1585. if ($file != '.' && $file != '..') {
  1586. $fullpath = $dir.'/'.$file;
  1587. if (!is_dir($fullpath)) {
  1588. unlink($fullpath);
  1589. } else {
  1590. deldir($fullpath);
  1591. }
  1592. }
  1593. }
  1594. closedir($dh);
  1595. if (rmdir($dir)) {
  1596. return true;
  1597. }
  1598. return false;
  1599. }
  1600. /**
  1601. * Export SCORM content into a zip file
  1602. *
  1603. * Basically, all this function does is put the scorm directory back into a zip file (like the one
  1604. * that was most probably used to import the course at first)
  1605. * @deprecated this function is only called in the newscorm/scorm_admin.php which is deprecated
  1606. *
  1607. * @param string Name of the SCORM path (or the directory under which it resides)
  1608. * @param array Not used right now. Should replace the use of global $_course
  1609. * @return void
  1610. * @author imandak80
  1611. */
  1612. function exportSCORM($scormname, $course) {
  1613. global $_course;
  1614. // Initialize.
  1615. $tmpname = api_get_path(SYS_COURSE_PATH).$_course['path'].'/scorm';
  1616. $zipfoldername = $tmpname.$scormname;
  1617. $zipfilename = $zipfoldername.'.zip';
  1618. // Create zipfile of given directory.
  1619. include_once api_get_path(LIBRARY_PATH).'pclzip/pclzip.lib.php';
  1620. $zip_folder = new PclZip($zipfilename);
  1621. $list = 1;
  1622. //$list = $zip_folder->create($zipfoldername.'/',PCLZIP_OPT_REMOVE_PATH,$tmpname.$scormname."/"); // whitout folder
  1623. $list = $zip_folder->create($zipfoldername.'/', PCLZIP_OPT_REMOVE_PATH, $tmpname);
  1624. if ($list == 0) {
  1625. //echo "Error : ".$zip_folder->errorInfo(true);
  1626. }
  1627. // Send to client.
  1628. DocumentManager :: file_send_for_download($zipfilename, false, basename($scormname.'.zip'));
  1629. // Clear.
  1630. include_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
  1631. my_delete($zipfilename);
  1632. }
  1633. /**
  1634. * This function returns an xml tag
  1635. * $data behaves as the content in case of full tags
  1636. * $data is an array of attributes in case of returning an opening tag
  1637. * @param string
  1638. * @param string
  1639. * @param array
  1640. * @param string
  1641. * @return string
  1642. */
  1643. function xmltagwrite($tagname, $which, $data, $linebreak = 'yes') {
  1644. switch ($which) {
  1645. case 'open':
  1646. $tag = '<'.$tagname;
  1647. $i = 0;
  1648. while ($data[0][$i]) {
  1649. $tag .= ' '.$data[0][$i]."=\"".$data[1][$i]."\"";
  1650. $i ++;
  1651. }
  1652. if ($tagname == 'file') {
  1653. $closing = '/';
  1654. }
  1655. $tag .= $closing.'>';
  1656. if ($linebreak != 'no_linebreak') {
  1657. $tag .= "\n";
  1658. }
  1659. break;
  1660. case 'close':
  1661. $tag = '</'.$tagname.'>';
  1662. if ($linebreak != 'no_linebreak') {
  1663. $tag .= "\n";
  1664. }
  1665. break;
  1666. case 'full':
  1667. $tag = '<'.$tagname;
  1668. $tag .= '>'.$data.'</'.$tagname.'>';
  1669. if ($linebreak != 'no_linebreak') {
  1670. $tag .= "\n";
  1671. }
  1672. break;
  1673. }
  1674. return $tag;
  1675. }
  1676. /**
  1677. * This function writes the imsmanifest.xml and exports the chapter names
  1678. * @param array Array containing filenames
  1679. * @param integer Learnpath_id
  1680. * @return void
  1681. */
  1682. function createimsmanifest($circle1_files, $learnpath_id) {
  1683. global $_course, $LPname, $expdir, $LPnamesafe;
  1684. //$tbl_learnpath_main, $tbl_learnpath_chapter, $tbl_learnpath_item,
  1685. $tbl_learnpath_main = Database :: get_course_table(TABLE_LEARNPATH_MAIN);
  1686. $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
  1687. $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
  1688. include_once '../metadata/md_funcs.php'; // RH: export metadata
  1689. // Header
  1690. // Charset should be dependent on content.
  1691. $header = '<?xml version="1.0" encoding="'.api_get_system_encoding().'"?>'."\n<manifest identifier='".$LPnamesafe."' version='1.1'\n xmlns='http://www.imsproject.org/xsd/imscp_rootv1p1p2'\n xmlns:adlcp='http://www.adlnet.org/xsd/adlcp_rootv1p2'\n xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n xsi:schemaLocation='http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd\n http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd\n http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd'>\n";
  1692. $org .= xmltagwrite('metadata', 'open');
  1693. $org .= ' '.xmltagwrite('schema', 'full', 'ADL SCORM');
  1694. $org .= ' '.xmltagwrite('schemaversion', 'full', '1.2');
  1695. $org .= xmltagwrite('metadata', 'close');
  1696. $defaultorgname = 'default_org';
  1697. $attributes[0][0] = 'default';
  1698. $attributes[1][0] = $defaultorgname;
  1699. $org .= xmltagwrite('organizations', 'open', $attributes);
  1700. $attributes[0][0] = 'identifier';
  1701. $attributes[1][0] = $defaultorgname;
  1702. $org .= ' '.xmltagwrite('organization', 'open', $attributes);
  1703. $org .= ' '.xmltagwrite('title', 'full', $LPname);
  1704. // Items list.
  1705. $i = 0;
  1706. $course_id = api_get_course_int_id();
  1707. $previous_item_id = '00';
  1708. while ($circle1_files[0][$i]) {
  1709. // Check whether we are in the border of two chapters.
  1710. //if (!$desc=strpos($circle1_files[2][$i],'scription')) { // This is needed if the descriptions are exported to file.
  1711. $sql = "SELECT * FROM $tbl_learnpath_item WHERE c_id = $course_id AND (id=".$circle1_files[2][$i].")";
  1712. $result = Database::query($sql);
  1713. $row = Database::fetch_array($result);
  1714. $parent_item_id = $row['parent_item_id'];
  1715. if ($parent_item_id != $previous_item_id) {
  1716. // We create the item tag for the chapter (without indifierref).
  1717. $sql2 = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND (id=".$parent_item_id.")";
  1718. $result2 = Database::query($sql2);
  1719. $row2 = Database::fetch_array($result2);
  1720. $chapter_name = $row2['chapter_name'];
  1721. $attributes = '';
  1722. $attributes[0][] = 'identifier';
  1723. $attributes[1][] = 'chapter_'.$row2['id'];
  1724. $attributes[0][] = 'isvisible';
  1725. $attributes[1][] = '1';
  1726. if ($previous_item_id != '00') {
  1727. $org .= ' '.xmltagwrite('item', 'close');
  1728. }
  1729. $org .= ' '.xmltagwrite('item', 'open', $attributes);
  1730. $org .= ' '.xmltagwrite('title', 'full', $chapter_name);
  1731. if ($row2['chapter_description'] != '') {
  1732. // Chapter description.
  1733. $attributes = '';
  1734. $attributes[0][] = 'identifier';
  1735. $attributes[1][] = 'chapter_'.$row2['id'].'_desc';
  1736. $attributes[0][] = 'isvisible';
  1737. $attributes[1][] = '1';
  1738. $org .= ' '.xmltagwrite('item', 'open', $attributes);
  1739. $org .= ' '.xmltagwrite('title', 'full', ' '.$row2['chapter_description']);
  1740. $org .= ' '.xmltagwrite('item', 'close');
  1741. }
  1742. }
  1743. $previous_item_id = $parent_item_id;
  1744. //}
  1745. $attributes = ''; // Item output.
  1746. $attributes[0][] = 'identifier';
  1747. $attributes[1][] = 'item_'.$circle1_files[2][$i];
  1748. $attributes[0][] = 'identifierref';
  1749. $attributes[1][] = 'item_ref_'.$circle1_files[2][$i];
  1750. $attributes[0][] = 'isvisible';
  1751. $attributes[1][] = '1';
  1752. $org .= ' '.xmltagwrite('item', 'open', $attributes);
  1753. $org .= ' '.xmltagwrite('title', 'full', $circle1_files[1][$i]);
  1754. if ($row['prereq_id'] != '') {
  1755. // Item prerequisites.
  1756. $attributes = '';
  1757. $attributes[0][] = 'type';
  1758. $attributes[1][] = 'aicc_script';
  1759. $org .= ' '.xmltagwrite('adlcp:prerequisites', 'open', $attributes, 'no_linebreak');
  1760. if ($row['prereq_type'] == 'i') {
  1761. $org .= 'item_'.$row['prereq_id'];
  1762. }
  1763. if ($row['prereq_type'] == 'c') {
  1764. $org .= 'chapter_'.$row['prereq_id'];
  1765. }
  1766. $org .= xmltagwrite('adlcp:prerequisites', 'close', $attributes);
  1767. }
  1768. if ($row['description'] != '') {
  1769. // Item description.
  1770. $attributes = '';
  1771. $attributes[0][] = 'identifier';
  1772. $attributes[1][] = 'item_'.$circle1_files[2][$i].'_desc';
  1773. $attributes[0][] = 'isvisible';
  1774. $attributes[1][] = '1';
  1775. $org .= ' '.xmltagwrite('item', 'open', $attributes);
  1776. $org .= ' '.xmltagwrite('title', 'full', ' '.$row['description']);
  1777. $org .= ' '.xmltagwrite('item', 'close');
  1778. }
  1779. $mds = new mdstore(true); // RH: export metadata; if no table, create it
  1780. if (($mdt = $mds->mds_get($row['item_type'].'.'.$row['item_id'])))
  1781. if (($mdo = api_strpos($mdt, '<metadata>')) && ($mdc = api_strpos($mdt, '</metadata>')))
  1782. $org .= ' '.api_substr($mdt, $mdo, $mdc - $mdo + 11)."\n";
  1783. $org .= ' '.xmltagwrite('item', 'close');
  1784. $i ++;
  1785. }
  1786. if ($circle1_files) {
  1787. $org .= ' '.xmltagwrite('item', 'close');
  1788. } // Not needed in case of a blank path.
  1789. $org .= ' '.xmltagwrite('organization', 'close');
  1790. $org .= xmltagwrite('organizations', 'close');
  1791. $org .= xmltagwrite('resources', 'open');
  1792. // Resources list.
  1793. $i = 0;
  1794. while ($circle1_files[0][$i]) {
  1795. $attributes = '';
  1796. $attributes[0][] = 'identifier';
  1797. $attributes[1][] = 'item_ref_'.$circle1_files[2][$i];
  1798. $attributes[0][] = 'type';
  1799. $attributes[1][] = 'webcontent';
  1800. $attributes[0][] = 'adlcp:scormtype';
  1801. $attributes[1][] = 'sco';
  1802. $attributes[0][] = 'href';
  1803. $attributes[1][] = $circle1_files[0][$i];
  1804. $org .= ' '.xmltagwrite('resource', 'open', $attributes);
  1805. $org .= ' '.xmltagwrite('metadata', 'open');
  1806. $org .= ' '.xmltagwrite('schema', 'full', 'ADL SCORM');
  1807. $org .= ' '.xmltagwrite('schemaversion', 'full', '1.2');
  1808. $org .= ' '.xmltagwrite('metadata', 'close');
  1809. $attributes = '';
  1810. $attributes[0][] = 'href';
  1811. $attributes[1][] = $circle1_files[0][$i];
  1812. $org .= ' '.xmltagwrite('file', 'open', $attributes);
  1813. $org .= ' '.xmltagwrite('resource', 'close');
  1814. $i ++;
  1815. }
  1816. $org .= xmltagwrite('resources', 'close');
  1817. $org .= xmltagwrite('manifest', 'close');
  1818. $manifest = $header.$org;
  1819. exporttofile('imsmanifest.xml', 'Manifest file', '0', $manifest);
  1820. }
  1821. /**
  1822. * Gets the tags of the file given as parameter
  1823. *
  1824. * if $filename is not found, GetSRCTags(filename) will return FALSE
  1825. * @param string File path
  1826. * @return mixed Array of strings on success, false on failure
  1827. * @author unknown
  1828. * @author Included by imandak80
  1829. */
  1830. function GetSRCTags($fileName) {
  1831. if (!($fp = fopen($fileName, 'r'))) {
  1832. // Iif file can't be opened, return false.
  1833. return false;
  1834. }
  1835. // Read file contents.
  1836. $contents = fread($fp, filesize($fileName));
  1837. fclose($fp);
  1838. $matches = array();
  1839. $srcList = array();
  1840. // Get all src tags contents in this file. Use multi-line search.
  1841. preg_match_all('/src(\s)*=(\s)*[\'"]([^\'"]*)[\'"]/mi', $contents, $matches); // Get the img src as contained between " or '
  1842. foreach ($matches[3] as $match) {
  1843. if (!in_array($match, $srcList)) {
  1844. $srcList[] = $match;
  1845. }
  1846. }
  1847. if (count($srcList) == 0) {
  1848. return false;
  1849. }
  1850. return $srcList;
  1851. }
  1852. /**
  1853. * Copy file and create directories in the path if needed.
  1854. *
  1855. * @param string $source Source path
  1856. * @param string $dest Destination path
  1857. * @return boolean true on success, false on failure
  1858. */
  1859. function CopyNCreate($source, $dest) {
  1860. if (strcmp($source, $dest) == 0)
  1861. return false;
  1862. $dir = '';
  1863. $tdest = explode('/', $dest);
  1864. for ($i = 0; $i < sizeof($tdest) - 1; $i ++) {
  1865. $dir = $dir.$tdest[$i].'/';
  1866. if (!is_dir($dir))
  1867. if (!mkdir($dir, api_get_permissions_for_new_directories()))
  1868. return false;
  1869. }
  1870. if (!copy($source, $dest))
  1871. return false;
  1872. return true;
  1873. }
  1874. function rcopy($source, $dest) {
  1875. //error_log($source." -> ".$dest, 0);
  1876. if (!file_exists($source)) {
  1877. //error_log($source." does not exist", 0);
  1878. return false;
  1879. }
  1880. if (is_dir($source)) {
  1881. //error_log($source." is a dir", 0);
  1882. // This is a directory.
  1883. // Remove trailing '/'
  1884. if (strrpos($source, '/') == sizeof($source) - 1) {
  1885. $source = substr($source, 0, size_of($source) - 1);
  1886. }
  1887. if (strrpos($dest, '/') == sizeof($dest) - 1) {
  1888. $dest = substr($dest, 0, size_of($dest) - 1);
  1889. }
  1890. if (!is_dir($dest)) {
  1891. $res = @mkdir($dest, api_get_permissions_for_new_directories());
  1892. if ($res !== false) {
  1893. return true;
  1894. } else {
  1895. // Remove latest part of path and try creating that.
  1896. if (rcopy(substr($source, 0, strrpos($source, '/')), substr($dest, 0, strrpos($dest, '/')))) {
  1897. return @mkdir($dest, api_get_permissions_for_new_directories());
  1898. } else {
  1899. return false;
  1900. }
  1901. }
  1902. }
  1903. return true;
  1904. } else {
  1905. // This is presumably a file.
  1906. //error_log($source." is a file", 0);
  1907. if (!@ copy($source, $dest)) {
  1908. //error_log("Could not simple-copy $source", 0);
  1909. $res = rcopy(dirname($source), dirname($dest));
  1910. if ($res === true) {
  1911. //error_log("Welcome dir created", 0);
  1912. return @ copy($source, $dest);
  1913. } else {
  1914. return false;
  1915. //error_log("Error creating path", 0);
  1916. }
  1917. } else {
  1918. //error_log("Could well simple-copy $source", 0);
  1919. return true;
  1920. }
  1921. }
  1922. }