exercise.class.php 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261
  1. <?php
  2. /*
  3. ==============================================================================
  4. Dokeos - elearning and course management software
  5. Copyright (c) 2004-2008 Dokeos SPRL
  6. For a full list of contributors, see "credits.txt".
  7. The full license can be read in "license.txt".
  8. This program is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU General Public License
  10. as published by the Free Software Foundation; either version 2
  11. of the License, or (at your option) any later version.
  12. See the GNU General Public License for more details.
  13. Contact address: Dokeos, rue du Corbeau, 108, B-1030 Brussels, Belgium
  14. Mail: info@dokeos.com
  15. ==============================================================================
  16. */
  17. /**
  18. * Exercise class: This class allows to instantiate an object of type Exercise
  19. * @package dokeos.exercise
  20. * @author Olivier Brouckaert
  21. * @version $Id: exercise.class.php 20200 2009-04-29 22:14:55Z cvargas1 $
  22. */
  23. if(!class_exists('Exercise')):
  24. class Exercise
  25. {
  26. var $id;
  27. var $exercise;
  28. var $description;
  29. var $sound;
  30. var $type;
  31. var $random;
  32. var $active;
  33. var $timeLimit;
  34. var $attempts;
  35. var $feedbacktype;
  36. var $end_time;
  37. var $start_time;
  38. var $questionList; // array with the list of this exercise's questions
  39. var $results_disabled;
  40. /**
  41. * constructor of the class
  42. *
  43. * @author - Olivier Brouckaert
  44. */
  45. function Exercise()
  46. {
  47. $this->id=0;
  48. $this->exercise='';
  49. $this->description='';
  50. $this->sound='';
  51. $this->type=1;
  52. $this->random=0;
  53. $this->active=1;
  54. $this->questionList=array();
  55. $this->timeLimit = 0;
  56. $this->end_time = '0000-00-00 00:00:00';
  57. $this->start_time = '0000-00-00 00:00:00';
  58. $this->results_disabled =1;
  59. }
  60. /**
  61. * reads exercise informations from the data base
  62. *
  63. * @author - Olivier Brouckaert
  64. * @param - integer $id - exercise ID
  65. * @return - boolean - true if exercise exists, otherwise false
  66. */
  67. function read($id)
  68. {
  69. global $_course;
  70. global $_configuration;
  71. global $questionList;
  72. $TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  73. $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
  74. $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
  75. #$TBL_REPONSES = Database::get_course_table(TABLE_QUIZ_ANSWER);
  76. $sql="SELECT title,description,sound,type,random,active, results_disabled, max_attempt,start_time,end_time,feedback_type FROM $TBL_EXERCICES WHERE id='".Database::escape_string($id)."'";
  77. $result=api_sql_query($sql,__FILE__,__LINE__);
  78. // if the exercise has been found
  79. if($object=Database::fetch_object($result))
  80. {
  81. $this->id=$id;
  82. $this->exercise=$object->title;
  83. $this->description=$object->description;
  84. $this->sound=$object->sound;
  85. $this->type=$object->type;
  86. $this->random=$object->random;
  87. $this->active=$object->active;
  88. $this->results_disabled =$object->results_disabled;
  89. $this->attempts = $object->max_attempt;
  90. $this->feedbacktype = $object->feedback_type;
  91. $this->end_time = $object->end_time;
  92. $this->start_time = $object->start_time;
  93. $sql="SELECT question_id, question_order FROM $TBL_EXERCICE_QUESTION,$TBL_QUESTIONS WHERE question_id=id AND exercice_id='".Database::escape_string($id)."' ORDER BY question_order";
  94. $result=api_sql_query($sql,__FILE__,__LINE__);
  95. // fills the array with the question ID for this exercise
  96. // the key of the array is the question position
  97. while($object=Database::fetch_object($result))
  98. {
  99. // makes sure that the question position is unique
  100. while(isset($this->questionList[$object->question_order]))
  101. {
  102. $object->question_order++;
  103. }
  104. $this->questionList[$object->question_order]=$object->question_id;
  105. }
  106. //var_dump($this->end_time,$object->start_time);
  107. if($this->random > 0){
  108. $this->questionList = $this->selectRandomList();
  109. }
  110. //overload questions list with recorded questions list
  111. //load questions only for exercises of type 'one question per page'
  112. //this is needed only is there is no questions
  113. //
  114. if($this->type == 2 && $_configuration['live_exercise_tracking']==true && $_SERVER['REQUEST_METHOD']!='POST' && defined('QUESTION_LIST_ALREADY_LOGGED'))
  115. {
  116. //if(empty($_SESSION['questionList']))
  117. $this->questionList = $questionList;
  118. }
  119. return true;
  120. }
  121. // exercise not found
  122. return false;
  123. }
  124. /**
  125. * returns the exercise ID
  126. *
  127. * @author - Olivier Brouckaert
  128. * @return - integer - exercise ID
  129. */
  130. function selectId()
  131. {
  132. return $this->id;
  133. }
  134. /**
  135. * returns the exercise title
  136. *
  137. * @author - Olivier Brouckaert
  138. * @return - string - exercise title
  139. */
  140. function selectTitle()
  141. {
  142. return $this->exercise;
  143. }
  144. /**
  145. * returns the number of attempts setted
  146. *
  147. * @return - numeric - exercise attempts
  148. */
  149. function selectAttempts()
  150. {
  151. return $this->attempts;
  152. }
  153. /** returns the number of FeedbackType *
  154. * 0=>Feedback , 1=>DirectFeedback, 2=>NoFeedback
  155. * @return - numeric - exercise attempts
  156. */
  157. function selectFeedbackType()
  158. {
  159. return $this->feedbacktype;
  160. }
  161. /**
  162. * returns the time limit
  163. */
  164. function selectTimeLimit()
  165. {
  166. return $this->timeLimit;
  167. }
  168. /**
  169. * returns the exercise description
  170. *
  171. * @author - Olivier Brouckaert
  172. * @return - string - exercise description
  173. */
  174. function selectDescription()
  175. {
  176. return $this->description;
  177. }
  178. /**
  179. * returns the exercise sound file
  180. *
  181. * @author - Olivier Brouckaert
  182. * @return - string - exercise description
  183. */
  184. function selectSound()
  185. {
  186. return $this->sound;
  187. }
  188. /**
  189. * returns the exercise type
  190. *
  191. * @author - Olivier Brouckaert
  192. * @return - integer - exercise type
  193. */
  194. function selectType()
  195. {
  196. return $this->type;
  197. }
  198. /**
  199. * tells if questions are selected randomly, and if so returns the draws
  200. *
  201. * @author - Carlos Vargas
  202. * @return - integer - results disabled exercise
  203. */
  204. function selectResultsDisabled()
  205. {
  206. return $this->results_disabled;
  207. }
  208. /**
  209. * tells if questions are selected randomly, and if so returns the draws
  210. *
  211. * @author - Olivier Brouckaert
  212. * @return - integer - 0 if not random, otherwise the draws
  213. */
  214. function isRandom()
  215. {
  216. if($this->random > 0){
  217. return true;
  218. }
  219. else{
  220. return false;
  221. }
  222. }
  223. /**
  224. * Same as isRandom() but has a name applied to values different than 0 or 1
  225. */
  226. function getShuffle()
  227. {
  228. return $this->random;
  229. }
  230. /**
  231. * returns the exercise status (1 = enabled ; 0 = disabled)
  232. *
  233. * @author - Olivier Brouckaert
  234. * @return - boolean - true if enabled, otherwise false
  235. */
  236. function selectStatus()
  237. {
  238. return $this->active;
  239. }
  240. /**
  241. * returns the array with the question ID list
  242. *
  243. * @author - Olivier Brouckaert
  244. * @return - array - question ID list
  245. */
  246. function selectQuestionList()
  247. {
  248. return $this->questionList;
  249. }
  250. /**
  251. * returns the number of questions in this exercise
  252. *
  253. * @author - Olivier Brouckaert
  254. * @return - integer - number of questions
  255. */
  256. function selectNbrQuestions()
  257. {
  258. return sizeof($this->questionList);
  259. }
  260. /**
  261. * selects questions randomly in the question list
  262. *
  263. * @author - Olivier Brouckaert
  264. * @return - array - if the exercise is not set to take questions randomly, returns the question list
  265. * without randomizing, otherwise, returns the list with questions selected randomly
  266. */
  267. function selectRandomList()
  268. {
  269. $nbQuestions = $this->selectNbrQuestions();
  270. $temp_list = $this->questionList;
  271. //error_log(print_r($temp_list,true));
  272. //error_log(count($temp_list));
  273. if (count($temp_list)<>0) {
  274. shuffle($temp_list);
  275. return array_combine(range(1,$nbQuestions),$temp_list);
  276. }
  277. $nbQuestions = $this->selectNbrQuestions();
  278. //Not a random exercise, or if there are not at least 2 questions
  279. if($this->random == 0 || $nbQuestions < 2)
  280. {
  281. return $this->questionList;
  282. }
  283. $randQuestionList = array();
  284. $alreadyChosen = array();
  285. for($i=0; $i < $this->random; $i++)
  286. {
  287. if($i < $nbQuestions){
  288. do
  289. {
  290. $rand=rand(1,$nbQuestions);
  291. }
  292. while(in_array($rand,$alreadyChosen));
  293. $alreadyChosen[]=$rand;
  294. $randQuestionList[$rand] = $this->questionList[$rand];
  295. }
  296. }
  297. return $randQuestionList;
  298. }
  299. /**
  300. * returns 'true' if the question ID is in the question list
  301. *
  302. * @author - Olivier Brouckaert
  303. * @param - integer $questionId - question ID
  304. * @return - boolean - true if in the list, otherwise false
  305. */
  306. function isInList($questionId)
  307. {
  308. if (is_array($this->questionList))
  309. return in_array($questionId,$this->questionList);
  310. else
  311. return false;
  312. }
  313. /**
  314. * changes the exercise title
  315. *
  316. * @author - Olivier Brouckaert
  317. * @param - string $title - exercise title
  318. */
  319. function updateTitle($title)
  320. {
  321. $this->exercise=$title;
  322. }
  323. /**
  324. * changes the exercise max attempts
  325. *
  326. * @param - numeric $attempts - exercise max attempts
  327. */
  328. function updateAttempts($attempts)
  329. {
  330. $this->attempts=$attempts;
  331. }
  332. /**
  333. * changes the exercise feedback type
  334. *
  335. * @param - numeric $attempts - exercise max attempts
  336. */
  337. function updateFeedbackType($feedback_type)
  338. {
  339. $this->feedbacktype=$feedback_type;
  340. }
  341. /**
  342. * changes the exercise description
  343. *
  344. * @author - Olivier Brouckaert
  345. * @param - string $description - exercise description
  346. */
  347. function updateDescription($description)
  348. {
  349. $this->description=$description;
  350. }
  351. /**
  352. * changes the exercise sound file
  353. *
  354. * @author - Olivier Brouckaert
  355. * @param - string $sound - exercise sound file
  356. * @param - string $delete - ask to delete the file
  357. */
  358. function updateSound($sound,$delete)
  359. {
  360. global $audioPath, $documentPath,$_course, $_user;
  361. $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
  362. $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
  363. if($sound['size'] && (strstr($sound['type'],'audio') || strstr($sound['type'],'video')))
  364. {
  365. $this->sound=$sound['name'];
  366. if(@move_uploaded_file($sound['tmp_name'],$audioPath.'/'.$this->sound))
  367. {
  368. $query="SELECT 1 FROM $TBL_DOCUMENT "
  369. ." WHERE path='".str_replace($documentPath,'',$audioPath).'/'.$this->sound."'";
  370. $result=api_sql_query($query,__FILE__,__LINE__);
  371. if(!mysql_num_rows($result))
  372. {
  373. /*$query="INSERT INTO $TBL_DOCUMENT(path,filetype) VALUES "
  374. ." ('".str_replace($documentPath,'',$audioPath).'/'.$this->sound."','file')";
  375. api_sql_query($query,__FILE__,__LINE__);*/
  376. $id = add_document($_course,str_replace($documentPath,'',$audioPath).'/'.$this->sound,'file',$sound['size'],$sound['name']);
  377. //$id = Database::get_last_insert_id();
  378. //$time = time();
  379. //$time = date("Y-m-d H:i:s", $time);
  380. // insert into the item_property table, using default visibility of "visible"
  381. /*$query = "INSERT INTO $TBL_ITEM_PROPERTY "
  382. ."(tool, ref, insert_user_id,to_group_id, insert_date, lastedit_date, lastedit_type) "
  383. ." VALUES "
  384. ."('".TOOL_DOCUMENT."', $id, $_user['user_id'], 0, '$time', '$time', 'DocumentAdded' )";
  385. api_sql_query($query,__FILE__,__LINE__);*/
  386. api_item_property_update($_course, TOOL_DOCUMENT, $id, 'DocumentAdded',$_user['user_id']);
  387. item_property_update_on_folder($_course,str_replace($documentPath,'',$audioPath),$_user['user_id']);
  388. }
  389. }
  390. }
  391. elseif($delete && is_file($audioPath.'/'.$this->sound))
  392. {
  393. $this->sound='';
  394. }
  395. }
  396. /**
  397. * changes the exercise type
  398. *
  399. * @author - Olivier Brouckaert
  400. * @param - integer $type - exercise type
  401. */
  402. function updateType($type)
  403. {
  404. $this->type=$type;
  405. }
  406. /**
  407. * sets to 0 if questions are not selected randomly
  408. * if questions are selected randomly, sets the draws
  409. *
  410. * @author - Olivier Brouckaert
  411. * @param - integer $random - 0 if not random, otherwise the draws
  412. */
  413. function setRandom($random)
  414. {
  415. $this->random=$random;
  416. }
  417. /**
  418. * enables the exercise
  419. *
  420. * @author - Olivier Brouckaert
  421. */
  422. function enable()
  423. {
  424. $this->active=1;
  425. }
  426. /**
  427. * disables the exercise
  428. *
  429. * @author - Olivier Brouckaert
  430. */
  431. function disable()
  432. {
  433. $this->active=0;
  434. }
  435. function disable_results()
  436. {
  437. $this->results_disabled = true;
  438. }
  439. function enable_results()
  440. {
  441. $this->results_disabled = false;
  442. }
  443. function updateResultsDisabled($results_disabled)
  444. {
  445. if ($results_disabled==1){
  446. $this->results_disabled = true;
  447. } else {
  448. $this->results_disabled = false;
  449. }
  450. }
  451. /**
  452. * updates the exercise in the data base
  453. *
  454. * @author - Olivier Brouckaert
  455. */
  456. function save($type_e='')
  457. {
  458. global $_course,$_user;
  459. $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
  460. $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
  461. $TBL_QUIZ_QUESTION= Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  462. $id=$this->id;
  463. $exercise=addslashes($this->exercise);
  464. $description=addslashes($this->description);
  465. $sound=addslashes($this->sound);
  466. $type=$this->type;
  467. $attempts=$this->attempts;
  468. $feedbacktype=$this->feedbacktype;
  469. $random=$this->random;
  470. $active=$this->active;
  471. if ($feedbacktype==1){
  472. $results_disabled = 1;
  473. } else {
  474. $results_disabled = intval($this->results_disabled);
  475. }
  476. $start_time = Database::escape_string($this->start_time);
  477. $end_time = Database::escape_string($this->end_time);
  478. // exercise already exists
  479. if($id)
  480. {
  481. $sql="UPDATE $TBL_EXERCICES SET
  482. title='".Database::escape_string($exercise)."',
  483. description='".Database::escape_string($description)."'";
  484. if ($type_e != 'simple') {
  485. $sql .= ", sound='".Database::escape_string($sound)."',
  486. type='".Database::escape_string($type)."',
  487. random='".Database::escape_string($random)."',
  488. active='".Database::escape_string($active)."',
  489. feedback_type='".Database::escape_string($feedbacktype)."',
  490. start_time='$start_time',end_time='$end_time',
  491. max_attempt='".Database::escape_string($attempts)."', " .
  492. "results_disabled='".Database::escape_string($results_disabled)."'";
  493. }
  494. $sql .= " WHERE id='".Database::escape_string($id)."'";
  495. // echo $sql;
  496. api_sql_query($sql,__FILE__,__LINE__);
  497. // update into the item_property table
  498. api_item_property_update($_course, TOOL_QUIZ, $id,'QuizUpdated',$_user['user_id']);
  499. if (api_get_setting('search_enabled')=='true') {
  500. $this -> search_engine_edit();
  501. }
  502. }
  503. // creates a new exercise
  504. else
  505. {
  506. $sql="INSERT INTO $TBL_EXERCICES(start_time,end_time,title,description,sound,type,random,active, results_disabled, max_attempt,feedback_type)
  507. VALUES(
  508. '$start_time','$end_time',
  509. '".Database::escape_string($exercise)."',
  510. '".Database::escape_string($description)."',
  511. '".Database::escape_string($sound)."',
  512. '".Database::escape_string($type)."',
  513. '".Database::escape_string($random)."',
  514. '".Database::escape_string($active)."',
  515. '".Database::escape_string($results_disabled)."',
  516. '".Database::escape_string($attempts)."',
  517. '".Database::escape_string($feedbacktype)."'
  518. )";
  519. api_sql_query($sql,__FILE__,__LINE__);
  520. $this->id=mysql_insert_id();
  521. // insert into the item_property table
  522. api_item_property_update($_course, TOOL_QUIZ, $this->id,'QuizAdded',$_user['user_id']);
  523. if (api_get_setting('search_enabled')=='true') {
  524. $this -> search_engine_save();
  525. }
  526. }
  527. // updates the question position
  528. $this->update_question_positions();
  529. }
  530. function update_question_positions() {
  531. // updates the question position
  532. $TBL_QUIZ_QUESTION= Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  533. foreach($this->questionList as $position=>$questionId)
  534. {
  535. //$sql="UPDATE $TBL_QUESTIONS SET position='".Database::escape_string($position)."' WHERE id='".Database::escape_string($questionId)."'";
  536. $sql="UPDATE $TBL_QUIZ_QUESTION SET question_order='".Database::escape_string($position)."' " .
  537. "WHERE question_id='".Database::escape_string($questionId)."' and exercice_id=".Database::escape_string($this->id)."";
  538. api_sql_query($sql,__FILE__,__LINE__);
  539. }
  540. }
  541. /**
  542. * moves a question up in the list
  543. *
  544. * @author - Olivier Brouckaert
  545. * @author - Julio Montoya (rewrote the code)
  546. * @param - integer $id - question ID to move up
  547. */
  548. function moveUp($id)
  549. {
  550. // there is a bug with some version of PHP with the key and prev functions
  551. // the script commented was tested in dev.dokeos.com with no success
  552. // Instead of using prev and next this was change with arrays.
  553. /*
  554. foreach($this->questionList as $position=>$questionId)
  555. {
  556. // if question ID found
  557. if($questionId == $id)
  558. {
  559. // position of question in the array
  560. echo $pos1=$position; //1
  561. echo "<br>";
  562. prev($this->questionList);
  563. prev($this->questionList);
  564. // position of previous question in the array
  565. $pos2=key($this->questionList);
  566. //if the cursor of the array hit the end
  567. // then we must reset the array to get the previous key
  568. if($pos2===null)
  569. {
  570. end($this->questionList);
  571. prev($this->questionList);
  572. $pos2=key($this->questionList);
  573. }
  574. // error, can't move question
  575. if(!$pos2)
  576. {
  577. //echo 'cant move!';
  578. $pos2=key($this->questionList);
  579. reset($this->questionList);
  580. }
  581. $id2=$this->questionList[$pos2];
  582. // exits foreach()
  583. break;
  584. }
  585. $i++;
  586. }
  587. */
  588. $question_list =array();
  589. foreach($this->questionList as $position=>$questionId)
  590. {
  591. $question_list[]= $questionId;
  592. }
  593. $len=count($question_list);
  594. $orderlist=array_keys($this->questionList);
  595. for($i=0;$i<$len;$i++)
  596. {
  597. $questionId = $question_list[$i];
  598. if($questionId == $id)
  599. {
  600. // position of question in the array
  601. $pos1=$orderlist[$i];
  602. $pos2=$orderlist[$i-1];
  603. if($pos2===null)
  604. {
  605. $pos2 = $orderlist[$len-1];
  606. }
  607. // error, can't move question
  608. if(!$pos2)
  609. {
  610. $pos2=$orderlist[0];
  611. $i=0;
  612. }
  613. break;
  614. }
  615. }
  616. // permutes questions in the array
  617. $temp=$this->questionList[$pos2];
  618. $this->questionList[$pos2]=$this->questionList[$pos1];
  619. $this->questionList[$pos1]=$temp;
  620. }
  621. /**
  622. * moves a question down in the list
  623. *
  624. * @author - Olivier Brouckaert
  625. * @param - integer $id - question ID to move down
  626. */
  627. function moveDown($id)
  628. {
  629. // there is a bug with some version of PHP with the key and prev functions
  630. // the script commented was tested in dev.dokeos.com with no success
  631. // Instead of using prev and next this was change with arrays.
  632. /*
  633. foreach($this->questionList as $position=>$questionId)
  634. {
  635. // if question ID found
  636. if($questionId == $id)
  637. {
  638. // position of question in the array
  639. $pos1=$position;
  640. //next($this->questionList);
  641. // position of next question in the array
  642. $pos2=key($this->questionList);
  643. // error, can't move question
  644. if(!$pos2)
  645. {
  646. //echo 'cant move!';
  647. return;
  648. }
  649. $id2=$this->questionList[$pos2];
  650. // exits foreach()
  651. break;
  652. }
  653. }
  654. */
  655. $question_list =array();
  656. foreach($this->questionList as $position=>$questionId)
  657. {
  658. $question_list[]= $questionId;
  659. }
  660. $len=count($question_list);
  661. $orderlist=array_keys($this->questionList);
  662. for($i=0;$i<$len;$i++)
  663. {
  664. $questionId = $question_list[$i];
  665. if($questionId == $id)
  666. {
  667. $pos1=$orderlist[$i+1];
  668. $pos2 =$orderlist[$i];
  669. if(!$pos2)
  670. {
  671. //echo 'cant move!';
  672. }
  673. break;
  674. }
  675. }
  676. // permutes questions in the array
  677. $temp=$this->questionList[$pos2];
  678. $this->questionList[$pos2]=$this->questionList[$pos1];
  679. $this->questionList[$pos1]=$temp;
  680. }
  681. /**
  682. * adds a question into the question list
  683. *
  684. * @author - Olivier Brouckaert
  685. * @param - integer $questionId - question ID
  686. * @return - boolean - true if the question has been added, otherwise false
  687. */
  688. function addToList($questionId)
  689. {
  690. // checks if the question ID is not in the list
  691. if(!$this->isInList($questionId))
  692. {
  693. // selects the max position
  694. if(!$this->selectNbrQuestions())
  695. {
  696. $pos=1;
  697. }
  698. else
  699. {
  700. if (is_array($this->questionList))
  701. $pos=max(array_keys($this->questionList))+1;
  702. }
  703. $this->questionList[$pos]=$questionId;
  704. return true;
  705. }
  706. return false;
  707. }
  708. /**
  709. * removes a question from the question list
  710. *
  711. * @author - Olivier Brouckaert
  712. * @param - integer $questionId - question ID
  713. * @return - boolean - true if the question has been removed, otherwise false
  714. */
  715. function removeFromList($questionId)
  716. {
  717. // searches the position of the question ID in the list
  718. $pos=array_search($questionId,$this->questionList);
  719. // question not found
  720. if($pos === false)
  721. {
  722. return false;
  723. }
  724. else
  725. {
  726. // deletes the position from the array containing the wanted question ID
  727. unset($this->questionList[$pos]);
  728. return true;
  729. }
  730. }
  731. /**
  732. * deletes the exercise from the database
  733. * Notice : leaves the question in the data base
  734. *
  735. * @author - Olivier Brouckaert
  736. */
  737. function delete(){
  738. global $_course,$_user;
  739. $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
  740. $sql="UPDATE $TBL_EXERCICES SET active='-1' WHERE id='".Database::escape_string($this->id)."'";
  741. api_sql_query($sql);
  742. api_item_property_update($_course, TOOL_QUIZ, $this->id,'QuizDeleted',$_user['user_id']);
  743. if (api_get_setting('search_enabled')=='true') {
  744. $this -> search_engine_delete();
  745. }
  746. }
  747. /**
  748. * Creates the form to create / edit an exercise
  749. * @param FormValidator $form the formvalidator instance (by reference)
  750. */
  751. function createForm ($form, $type='full')
  752. {
  753. global $id;
  754. if(empty($type)){
  755. $type='full';
  756. }
  757. // form title
  758. if (!empty($_GET['exerciseId'])) {
  759. $form_title = get_lang('ModifyExercise');
  760. } else {
  761. $form_title = get_lang('NewEx');
  762. }
  763. $form->addElement('header', '', $form_title);
  764. // title
  765. $form -> addElement('text', 'exerciseTitle', get_lang('ExerciseName'),'class="input_titles"');
  766. $form->applyFilter('exerciseTitle','html_filter');
  767. // fck editor
  768. global $fck_attribute;
  769. $fck_attribute = array();
  770. $fck_attribute['Width'] = '100%';
  771. $fck_attribute['Height'] = '200px';
  772. $fck_attribute['ToolbarSet'] = 'TestDescription';
  773. $form -> addElement('html','<div class="row">
  774. <div class="label"></div>
  775. <div class="formw" style="height:50px">
  776. <a href="javascript://" onclick=" return show_media()"> <span id="media_icon"> <img src="../img/looknfeel.png" alt="" />&nbsp;'.get_lang('ExerciseDescription').'</span></a>
  777. </div>
  778. </div>');
  779. $form -> addElement ('html','<div id="media" style="display:none;">');
  780. $form -> addElement ('html_editor', 'exerciseDescription');
  781. $form -> addElement ('html','</div>');
  782. $form -> addElement('html','<div class="row">
  783. <div class="label">&nbsp;</div>
  784. <div class="formw">
  785. <a href="javascript://" onclick=" return advanced_parameters()"><span id="img_plus_and_minus"><div style="vertical-align:top" ><img src="../img/div_show.gif" alt="" />&nbsp;'.get_lang('AdvancedParameters').'</div></span></a>
  786. </div>
  787. </div>');
  788. // Random questions
  789. $form -> addElement('html','<div id="options" style="display:none">');
  790. if($type=='full') {
  791. // feedback type
  792. $radios_feedback = array();
  793. $radios_feedback[] = FormValidator :: createElement ('radio', 'exerciseFeedbackType', null, get_lang('ExerciseAtTheEndOfTheTest'),'0');
  794. $radios_feedback[] = FormValidator :: createElement ('radio', 'exerciseFeedbackType', null, get_lang('NoFeedback'),'2');
  795. $form -> addGroup($radios_feedback, null, get_lang('FeedbackType'));
  796. $feedback_option[0]=get_lang('ExerciseAtTheEndOfTheTest');
  797. $feedback_option[1]=get_lang('DirectFeedback');
  798. $feedback_option[2]=get_lang('NoFeedback');
  799. //Can't modify a DirectFeedback question
  800. if ($this->selectFeedbackType() != 1 ) {
  801. // $form -> addElement('select', 'exerciseFeedbackType',get_lang('FeedbackType'),$feedback_option,'onchange="javascript:feedbackselection()"');
  802. // test type
  803. $radios = array();
  804. $radios[] = FormValidator :: createElement ('radio', 'exerciseType', null, get_lang('QuestionsPerPageOne'),'2');
  805. $radios[] = FormValidator :: createElement ('radio', 'exerciseType', null, get_lang('QuestionsPerPageAll'),'1');
  806. $form -> addGroup($radios, null, get_lang('QuestionsPerPage'));
  807. } else {
  808. // if is Directfeedback but has not questions we can allow to modify the question type
  809. if ($this->selectNbrQuestions()== 0) {
  810. $form -> addElement('select', 'exerciseFeedbackType',get_lang('FeedbackType'),$feedback_option,'onchange="javascript:feedbackselection()"');
  811. // test type
  812. $radios = array();
  813. $radios[] = FormValidator :: createElement ('radio', 'exerciseType', null, get_lang('SimpleExercise'),'1');
  814. $radios[] = FormValidator :: createElement ('radio', 'exerciseType', null, get_lang('SequentialExercise'),'2');
  815. $form -> addGroup($radios, null, get_lang('ExerciseType'));
  816. } else {
  817. //we force the options to the DirectFeedback exercisetype
  818. $form -> addElement('hidden', 'exerciseFeedbackType','1');
  819. $form -> addElement('hidden', 'exerciseType','2');
  820. }
  821. }
  822. $radios_results_disabled = array();
  823. $radios_results_disabled[] = FormValidator :: createElement ('radio', 'results_disabled', null, get_lang('Yes'),'0');
  824. $radios_results_disabled[] = FormValidator :: createElement ('radio', 'results_disabled', null, get_lang('No'),'1');
  825. $form -> addGroup($radios_results_disabled, null, get_lang('ShowResultsToStudents'));
  826. $random = array();
  827. $option=array();
  828. $max = ($this->id > 0) ? $this->selectNbrQuestions() : 10 ;
  829. $option = range(0,$max);
  830. $option[0]=get_lang('No');
  831. $random[] = FormValidator :: createElement ('select', 'randomQuestions',null,$option);
  832. $random[] = FormValidator :: createElement ('static', 'help','help','<span style="font-style: italic;">'.get_lang('RandomQuestionsHelp').'</span>');
  833. //$random[] = FormValidator :: createElement ('text', 'randomQuestions', null,null,'0');
  834. $form -> addGroup($random,null,get_lang('RandomQuestions'),'<br />');
  835. $attempt_option=range(0,10);
  836. $attempt_option[0]=get_lang('Infinite');
  837. $form -> addElement('select', 'exerciseAttempts',get_lang('ExerciseAttempts'),$attempt_option);
  838. $form -> addElement('checkbox', 'enabletimelimit',get_lang('EnableTimeLimits'),null,'onclick = " return timelimit() "');
  839. $var= Exercise::selectTimeLimit();
  840. $form -> addElement('html','</div>');
  841. if(($this -> start_time!='0000-00-00 00:00:00')||($this -> end_time!='0000-00-00 00:00:00'))
  842. $form -> addElement('html','<div id="options2" style="display:block;">');
  843. else
  844. $form -> addElement('html','<div id="options2" style="display:none;">');
  845. //$form -> addElement('date', 'start_time', get_lang('ExeStartTime'), array('language'=>'es','format' => 'dMYHi'));
  846. //$form -> addElement('date', 'end_time', get_lang('ExeEndTime'), array('language'=>'es','format' => 'dMYHi'));
  847. $form->addElement('datepicker', 'start_time', get_lang('ExeStartTime'), array('form_name'=>'exercise_admin'));
  848. $form->addElement('datepicker', 'end_time', get_lang('ExeEndTime'), array('form_name'=>'exercise_admin'));
  849. //$form -> addElement('text', 'exerciseAttempts', get_lang('ExerciseAttempts').' : ',array('size'=>'2'));
  850. $form -> addElement('html','</div>');
  851. $defaults = array();
  852. if (api_get_setting('search_enabled') === 'true') {
  853. require_once(api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php');
  854. $form -> addElement ('checkbox', 'index_document','', get_lang('SearchFeatureDoIndexDocument'));
  855. $form -> addElement ('html','<br /><div class="row">');
  856. $form -> addElement ('html', '<div class="label">'. get_lang('SearchFeatureDocumentLanguage') .'</div>');
  857. $form -> addElement ('html', '<div class="formw">'. api_get_languages_combo() .'</div>');
  858. $form -> addElement ('html','</div><div class="sub-form">');
  859. $specific_fields = get_specific_field_list();
  860. foreach ($specific_fields as $specific_field) {
  861. $form -> addElement ('text', $specific_field['code'], $specific_field['name']);
  862. $filter = array('course_code'=> "'". api_get_course_id() ."'", 'field_id' => $specific_field['id'], 'ref_id' => $this->id, 'tool_id' => '\''. TOOL_QUIZ .'\'');
  863. $values = get_specific_field_values_list($filter, array('value'));
  864. if ( !empty($values) ) {
  865. $arr_str_values = array();
  866. foreach ($values as $value) {
  867. $arr_str_values[] = $value['value'];
  868. }
  869. $defaults[$specific_field['code']] = implode(', ', $arr_str_values);
  870. }
  871. }
  872. $form -> addElement ('html','</div>');
  873. }
  874. }
  875. // submit
  876. isset($_GET['exerciseId'])?$text=get_lang('ModifyExercise'):$text=get_lang('ProcedToQuestions');
  877. $form -> addElement('html', '<br /><br />');
  878. $form -> addElement('style_submit_button', 'submitExercise', $text, 'class="save"');
  879. $form -> addRule ('exerciseTitle', get_lang('GiveExerciseName'), 'required');
  880. if($type=='full') {
  881. // rules
  882. $form -> addRule ('exerciseAttempts', get_lang('Numeric'), 'numeric');
  883. $form -> addRule ('start_time', get_lang('InvalidDate'), 'date');
  884. $form -> addRule ('end_time', get_lang('InvalidDate'), 'date');
  885. $form -> addRule(array ('start_time', 'end_time'), get_lang('StartDateShouldBeBeforeEndDate'), 'date_compare', 'lte');
  886. }
  887. // defaults
  888. if($type=='full') {
  889. if($this -> id > 0) {
  890. if ($this -> random > $this->selectNbrQuestions()) {
  891. $defaults['randomQuestions'] = $this->selectNbrQuestions();
  892. } else {
  893. $defaults['randomQuestions'] = $this -> random;
  894. }
  895. $defaults['exerciseType'] = $this -> selectType();
  896. $defaults['exerciseTitle'] = $this -> selectTitle();
  897. $defaults['exerciseDescription'] = $this -> selectDescription();
  898. $defaults['exerciseAttempts'] = $this->selectAttempts();
  899. $defaults['exerciseFeedbackType'] = $this->selectFeedbackType();
  900. $defaults['results_disabled'] = $this->selectResultsDisabled();
  901. if(($this -> start_time!='0000-00-00 00:00:00')||($this -> end_time!='0000-00-00 00:00:00'))
  902. $defaults['enabletimelimit'] = 1;
  903. $defaults['start_time'] = ($this->start_time!='0000-00-00 00:00:00')? $this -> start_time : date('Y-m-d 12:00:00');
  904. $defaults['end_time'] = ($this->end_time!='0000-00-00 00:00:00')?$this -> end_time : date('Y-m-d 12:00:00');
  905. } else {
  906. $defaults['exerciseType'] = 2;
  907. $defaults['exerciseAttempts'] = 0;
  908. $defaults['randomQuestions'] = 0;
  909. $defaults['exerciseDescription'] = '';
  910. $defaults['exerciseFeedbackType'] = 0;
  911. $defaults['results_disabled'] = 0;
  912. $defaults['start_time'] = date('Y-m-d 12:00:00');
  913. $defaults['end_time'] = date('Y-m-d 12:00:00');
  914. }
  915. } else {
  916. $defaults['exerciseTitle'] = $this -> selectTitle();
  917. $defaults['exerciseDescription'] = $this -> selectDescription();
  918. }
  919. if (api_get_setting('search_enabled') === 'true') {
  920. $defaults['index_document'] = 'checked="checked"';
  921. }
  922. $form -> setDefaults($defaults);
  923. }
  924. /**
  925. * function which process the creation of exercises
  926. * @param FormValidator $form the formvalidator instance
  927. */
  928. function processCreation($form,$type='')
  929. {
  930. $this -> updateTitle($form -> getSubmitValue('exerciseTitle'));
  931. $this -> updateDescription($form -> getSubmitValue('exerciseDescription'));
  932. $this -> updateAttempts($form -> getSubmitValue('exerciseAttempts'));
  933. $this -> updateFeedbackType($form -> getSubmitValue('exerciseFeedbackType'));
  934. $this -> updateType($form -> getSubmitValue('exerciseType'));
  935. $this -> setRandom($form -> getSubmitValue('randomQuestions'));
  936. $this -> updateResultsDisabled($form -> getSubmitValue('results_disabled'));
  937. if($form -> getSubmitValue('enabletimelimit')==1)
  938. {
  939. $start_time = $form -> getSubmitValue('start_time');
  940. $this->start_time = $start_time['Y'].'-'.$start_time['F'].'-'.$start_time['d'].' '.$start_time['H'].':'.$start_time['i'].':00';
  941. $end_time = $form -> getSubmitValue('end_time');
  942. $this->end_time = $end_time['Y'].'-'.$end_time['F'].'-'.$end_time['d'].' '.$end_time['H'].':'.$end_time['i'].':00';
  943. }
  944. else
  945. {
  946. $this->start_time = '0000-00-00 00:00:00';
  947. $this->end_time = '0000-00-00 00:00:00';
  948. }
  949. //echo $end_time;exit;
  950. $this -> save($type);
  951. }
  952. function search_engine_save() {
  953. if ($_POST['index_document'] != 1) {
  954. return;
  955. }
  956. $course_id = api_get_course_id();
  957. require_once(api_get_path(LIBRARY_PATH) . 'search/DokeosIndexer.class.php');
  958. require_once(api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php');
  959. require_once(api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php');
  960. $specific_fields = get_specific_field_list();
  961. $ic_slide = new IndexableChunk();
  962. $all_specific_terms = '';
  963. foreach ($specific_fields as $specific_field) {
  964. if (isset($_REQUEST[$specific_field['code']])) {
  965. $sterms = trim($_REQUEST[$specific_field['code']]);
  966. if (!empty($sterms)) {
  967. $all_specific_terms .= ' '. $sterms;
  968. $sterms = explode(',', $sterms);
  969. foreach ($sterms as $sterm) {
  970. $ic_slide->addTerm(trim($sterm), $specific_field['code']);
  971. add_specific_field_value($specific_field['id'], $course_id, TOOL_QUIZ, $this->id, $sterm);
  972. }
  973. }
  974. }
  975. }
  976. // build the chunk to index
  977. $ic_slide->addValue("title", $this->exercise);
  978. $ic_slide->addCourseId($course_id);
  979. $ic_slide->addToolId(TOOL_QUIZ);
  980. $xapian_data = array(
  981. SE_COURSE_ID => $course_id,
  982. SE_TOOL_ID => TOOL_QUIZ,
  983. SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_EXERCISE, 'exercise_id' => (int)$this->id),
  984. SE_USER => (int)api_get_user_id(),
  985. );
  986. $ic_slide->xapian_data = serialize($xapian_data);
  987. $exercise_description = $all_specific_terms .' '. $this->description;
  988. $ic_slide->addValue("content", $exercise_description);
  989. $di = new DokeosIndexer();
  990. isset($_POST['language'])? $lang=Database::escape_string($_POST['language']): $lang = 'english';
  991. $di->connectDb(NULL, NULL, $lang);
  992. $di->addChunk($ic_slide);
  993. //index and return search engine document id
  994. $did = $di->index();
  995. if ($did) {
  996. // save it to db
  997. $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  998. $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
  999. VALUES (NULL , \'%s\', \'%s\', %s, %s)';
  1000. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id, $did);
  1001. api_sql_query($sql,__FILE__,__LINE__);
  1002. }
  1003. }
  1004. function search_engine_edit() {
  1005. // update search enchine and its values table if enabled
  1006. if (api_get_setting('search_enabled')=='true') {
  1007. $course_id = api_get_course_id();
  1008. // actually, it consists on delete terms from db, insert new ones, create a new search engine document, and remove the old one
  1009. // get search_did
  1010. $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  1011. $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
  1012. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1013. $res = api_sql_query($sql, __FILE__, __LINE__);
  1014. if (Database::num_rows($res) > 0) {
  1015. require_once(api_get_path(LIBRARY_PATH) . 'search/DokeosIndexer.class.php');
  1016. require_once(api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php');
  1017. require_once(api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php');
  1018. $se_ref = Database::fetch_array($res);
  1019. $specific_fields = get_specific_field_list();
  1020. $ic_slide = new IndexableChunk();
  1021. $all_specific_terms = '';
  1022. foreach ($specific_fields as $specific_field) {
  1023. delete_all_specific_field_value($course_id, $specific_field['id'], TOOL_QUIZ, $this->id);
  1024. if (isset($_REQUEST[$specific_field['code']])) {
  1025. $sterms = trim($_REQUEST[$specific_field['code']]);
  1026. $all_specific_terms .= ' '. $sterms;
  1027. $sterms = explode(',', $sterms);
  1028. foreach ($sterms as $sterm) {
  1029. $ic_slide->addTerm(trim($sterm), $specific_field['code']);
  1030. add_specific_field_value($specific_field['id'], $course_id, TOOL_QUIZ, $this->id, $sterm);
  1031. }
  1032. }
  1033. }
  1034. // build the chunk to index
  1035. $ic_slide->addValue("title", $this->exercise);
  1036. $ic_slide->addCourseId($course_id);
  1037. $ic_slide->addToolId(TOOL_QUIZ);
  1038. $xapian_data = array(
  1039. SE_COURSE_ID => $course_id,
  1040. SE_TOOL_ID => TOOL_QUIZ,
  1041. SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_EXERCISE, 'exercise_id' => (int)$this->id),
  1042. SE_USER => (int)api_get_user_id(),
  1043. );
  1044. $ic_slide->xapian_data = serialize($xapian_data);
  1045. $exercise_description = $all_specific_terms .' '. $this->description;
  1046. $ic_slide->addValue("content", $exercise_description);
  1047. $di = new DokeosIndexer();
  1048. isset($_POST['language'])? $lang=Database::escape_string($_POST['language']): $lang = 'english';
  1049. $di->connectDb(NULL, NULL, $lang);
  1050. $di->remove_document((int)$se_ref['search_did']);
  1051. $di->addChunk($ic_slide);
  1052. //index and return search engine document id
  1053. $did = $di->index();
  1054. if ($did) {
  1055. // save it to db
  1056. $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=\'%s\'';
  1057. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1058. api_sql_query($sql,__FILE__,__LINE__);
  1059. //var_dump($sql);
  1060. $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
  1061. VALUES (NULL , \'%s\', \'%s\', %s, %s)';
  1062. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id, $did);
  1063. api_sql_query($sql,__FILE__,__LINE__);
  1064. }
  1065. }
  1066. }
  1067. }
  1068. function search_engine_delete() {
  1069. // remove from search engine if enabled
  1070. if (api_get_setting('search_enabled') == 'true') {
  1071. $course_id = api_get_course_id();
  1072. $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  1073. $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level IS NULL LIMIT 1';
  1074. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1075. $res = api_sql_query($sql, __FILE__, __LINE__);
  1076. if (Database::num_rows($res) > 0) {
  1077. $row = Database::fetch_array($res);
  1078. require_once(api_get_path(LIBRARY_PATH) .'search/DokeosIndexer.class.php');
  1079. $di = new DokeosIndexer();
  1080. $di->remove_document((int)$row['search_did']);
  1081. unset($di);
  1082. $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
  1083. foreach ( $this->questionList as $question_i) {
  1084. $sql = 'SELECT type FROM %s WHERE id=%s';
  1085. $sql = sprintf($sql, $tbl_quiz_question, $question_i);
  1086. $qres = api_sql_query($sql, __FILE__, __LINE__);
  1087. if (Database::num_rows($qres) > 0) {
  1088. $qrow = Database::fetch_array($qres);
  1089. $objQuestion = Question::getInstance($qrow['type']);
  1090. $objQuestion = Question::read((int)$question_i);
  1091. $objQuestion->search_engine_edit($this->id, FALSE, TRUE);
  1092. unset($objQuestion);
  1093. }
  1094. }
  1095. }
  1096. $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level IS NULL LIMIT 1';
  1097. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1098. api_sql_query($sql, __FILE__, __LINE__);
  1099. // remove terms from db
  1100. require_once(api_get_path(LIBRARY_PATH) .'specific_fields_manager.lib.php');
  1101. delete_all_values_for_item($course_id, TOOL_QUIZ, $this->id);
  1102. }
  1103. }
  1104. }
  1105. endif;
  1106. ?>