learnpath_functions.inc.php 99 KB

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