exercise.class.php 41 KB

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