exercise.class.php 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251
  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=api_sql_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=api_sql_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=api_sql_query($query,__FILE__,__LINE__);
  357. if(!mysql_num_rows($result))
  358. {
  359. /*$query="INSERT INTO $TBL_DOCUMENT(path,filetype) VALUES "
  360. ." ('".str_replace($documentPath,'',$audioPath).'/'.$this->sound."','file')";
  361. api_sql_query($query,__FILE__,__LINE__);*/
  362. $id = add_document($_course,str_replace($documentPath,'',$audioPath).'/'.$this->sound,'file',$sound['size'],$sound['name']);
  363. //$id = Database::get_last_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. api_sql_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. if ($feedbacktype==1){
  458. $results_disabled = 1;
  459. } else {
  460. $results_disabled = intval($this->results_disabled);
  461. }
  462. $start_time = Database::escape_string($this->start_time);
  463. $end_time = Database::escape_string($this->end_time);
  464. // exercise already exists
  465. if($id) {
  466. /*
  467. title='".Database::escape_string(Security::remove_XSS($exercise))."',
  468. description='".Database::escape_string(Security::remove_XSS(api_html_entity_decode($description),COURSEMANAGERLOWSECURITY))."'";
  469. */
  470. $sql="UPDATE $TBL_EXERCICES SET
  471. title='".Database::escape_string($exercise)."',
  472. description='".Database::escape_string($description)."'";
  473. if ($type_e != 'simple') {
  474. $sql .= ", sound='".Database::escape_string($sound)."',
  475. type='".Database::escape_string($type)."',
  476. random='".Database::escape_string($random)."',
  477. active='".Database::escape_string($active)."',
  478. feedback_type='".Database::escape_string($feedbacktype)."',
  479. start_time='$start_time',end_time='$end_time',
  480. max_attempt='".Database::escape_string($attempts)."', " .
  481. "results_disabled='".Database::escape_string($results_disabled)."'";
  482. }
  483. $sql .= " WHERE id='".Database::escape_string($id)."'";
  484. // echo $sql;
  485. api_sql_query($sql,__FILE__,__LINE__);
  486. // update into the item_property table
  487. api_item_property_update($_course, TOOL_QUIZ, $id,'QuizUpdated',$_user['user_id']);
  488. if (api_get_setting('search_enabled')=='true') {
  489. $this -> search_engine_edit();
  490. }
  491. } else {// creates a new exercise
  492. //add condition by anonymous user
  493. /*if (!api_is_anonymous()) {
  494. //is course manager
  495. $cond1=Database::escape_string($exercise);
  496. $cond2=Database::escape_string($description);
  497. } else {
  498. //is anonymous user
  499. $cond1=Database::escape_string(Security::remove_XSS($exercise));
  500. $cond2=Database::escape_string(Security::remove_XSS(api_html_entity_decode($description),COURSEMANAGERLOWSECURITY));
  501. }*/
  502. $sql="INSERT INTO $TBL_EXERCICES (start_time,end_time,title,description,sound,type,random,active, results_disabled, max_attempt,feedback_type)
  503. VALUES(
  504. '$start_time','$end_time',
  505. '".Database::escape_string($exercise)."',
  506. '".Database::escape_string($description)."',
  507. '".Database::escape_string($sound)."',
  508. '".Database::escape_string($type)."',
  509. '".Database::escape_string($random)."',
  510. '".Database::escape_string($active)."',
  511. '".Database::escape_string($results_disabled)."',
  512. '".Database::escape_string($attempts)."',
  513. '".Database::escape_string($feedbacktype)."'
  514. )";
  515. api_sql_query($sql,__FILE__,__LINE__);
  516. $this->id=Database::insert_id();
  517. // insert into the item_property table
  518. api_item_property_update($_course, TOOL_QUIZ, $this->id,'QuizAdded',$_user['user_id']);
  519. if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian')) {
  520. $this -> search_engine_save();
  521. }
  522. }
  523. // updates the question position
  524. $this->update_question_positions();
  525. }
  526. function update_question_positions() {
  527. // updates the question position
  528. $TBL_QUIZ_QUESTION= Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  529. foreach($this->questionList as $position=>$questionId)
  530. {
  531. //$sql="UPDATE $TBL_QUESTIONS SET position='".Database::escape_string($position)."' WHERE id='".Database::escape_string($questionId)."'";
  532. $sql="UPDATE $TBL_QUIZ_QUESTION SET question_order='".Database::escape_string($position)."' " .
  533. "WHERE question_id='".Database::escape_string($questionId)."' and exercice_id=".Database::escape_string($this->id)."";
  534. api_sql_query($sql,__FILE__,__LINE__);
  535. }
  536. }
  537. /**
  538. * moves a question up in the list
  539. *
  540. * @author - Olivier Brouckaert
  541. * @author - Julio Montoya (rewrote the code)
  542. * @param - integer $id - question ID to move up
  543. */
  544. function moveUp($id)
  545. {
  546. // there is a bug with some version of PHP with the key and prev functions
  547. // the script commented was tested in dev.dokeos.com with no success
  548. // Instead of using prev and next this was change with arrays.
  549. /*
  550. foreach($this->questionList as $position=>$questionId)
  551. {
  552. // if question ID found
  553. if($questionId == $id)
  554. {
  555. // position of question in the array
  556. echo $pos1=$position; //1
  557. echo "<br>";
  558. prev($this->questionList);
  559. prev($this->questionList);
  560. // position of previous question in the array
  561. $pos2=key($this->questionList);
  562. //if the cursor of the array hit the end
  563. // then we must reset the array to get the previous key
  564. if($pos2===null)
  565. {
  566. end($this->questionList);
  567. prev($this->questionList);
  568. $pos2=key($this->questionList);
  569. }
  570. // error, can't move question
  571. if(!$pos2)
  572. {
  573. //echo 'cant move!';
  574. $pos2=key($this->questionList);
  575. reset($this->questionList);
  576. }
  577. $id2=$this->questionList[$pos2];
  578. // exits foreach()
  579. break;
  580. }
  581. $i++;
  582. }
  583. */
  584. $question_list =array();
  585. foreach($this->questionList as $position=>$questionId)
  586. {
  587. $question_list[]= $questionId;
  588. }
  589. $len=count($question_list);
  590. $orderlist=array_keys($this->questionList);
  591. for($i=0;$i<$len;$i++)
  592. {
  593. $questionId = $question_list[$i];
  594. if($questionId == $id)
  595. {
  596. // position of question in the array
  597. $pos1=$orderlist[$i];
  598. $pos2=$orderlist[$i-1];
  599. if($pos2===null)
  600. {
  601. $pos2 = $orderlist[$len-1];
  602. }
  603. // error, can't move question
  604. if(!$pos2)
  605. {
  606. $pos2=$orderlist[0];
  607. $i=0;
  608. }
  609. break;
  610. }
  611. }
  612. // permutes questions in the array
  613. $temp=$this->questionList[$pos2];
  614. $this->questionList[$pos2]=$this->questionList[$pos1];
  615. $this->questionList[$pos1]=$temp;
  616. }
  617. /**
  618. * moves a question down in the list
  619. *
  620. * @author - Olivier Brouckaert
  621. * @param - integer $id - question ID to move down
  622. */
  623. function moveDown($id)
  624. {
  625. // there is a bug with some version of PHP with the key and prev functions
  626. // the script commented was tested in dev.dokeos.com with no success
  627. // Instead of using prev and next this was change with arrays.
  628. /*
  629. foreach($this->questionList as $position=>$questionId)
  630. {
  631. // if question ID found
  632. if($questionId == $id)
  633. {
  634. // position of question in the array
  635. $pos1=$position;
  636. //next($this->questionList);
  637. // position of next question in the array
  638. $pos2=key($this->questionList);
  639. // error, can't move question
  640. if(!$pos2)
  641. {
  642. //echo 'cant move!';
  643. return;
  644. }
  645. $id2=$this->questionList[$pos2];
  646. // exits foreach()
  647. break;
  648. }
  649. }
  650. */
  651. $question_list =array();
  652. foreach($this->questionList as $position=>$questionId)
  653. {
  654. $question_list[]= $questionId;
  655. }
  656. $len=count($question_list);
  657. $orderlist=array_keys($this->questionList);
  658. for($i=0;$i<$len;$i++)
  659. {
  660. $questionId = $question_list[$i];
  661. if($questionId == $id)
  662. {
  663. $pos1=$orderlist[$i+1];
  664. $pos2 =$orderlist[$i];
  665. if(!$pos2)
  666. {
  667. //echo 'cant move!';
  668. }
  669. break;
  670. }
  671. }
  672. // permutes questions in the array
  673. $temp=$this->questionList[$pos2];
  674. $this->questionList[$pos2]=$this->questionList[$pos1];
  675. $this->questionList[$pos1]=$temp;
  676. }
  677. /**
  678. * adds a question into the question list
  679. *
  680. * @author - Olivier Brouckaert
  681. * @param - integer $questionId - question ID
  682. * @return - boolean - true if the question has been added, otherwise false
  683. */
  684. function addToList($questionId)
  685. {
  686. // checks if the question ID is not in the list
  687. if(!$this->isInList($questionId))
  688. {
  689. // selects the max position
  690. if(!$this->selectNbrQuestions())
  691. {
  692. $pos=1;
  693. }
  694. else
  695. {
  696. if (is_array($this->questionList))
  697. $pos=max(array_keys($this->questionList))+1;
  698. }
  699. $this->questionList[$pos]=$questionId;
  700. return true;
  701. }
  702. return false;
  703. }
  704. /**
  705. * removes a question from the question list
  706. *
  707. * @author - Olivier Brouckaert
  708. * @param - integer $questionId - question ID
  709. * @return - boolean - true if the question has been removed, otherwise false
  710. */
  711. function removeFromList($questionId)
  712. {
  713. // searches the position of the question ID in the list
  714. $pos=array_search($questionId,$this->questionList);
  715. // question not found
  716. if($pos === false)
  717. {
  718. return false;
  719. }
  720. else
  721. {
  722. // deletes the position from the array containing the wanted question ID
  723. unset($this->questionList[$pos]);
  724. return true;
  725. }
  726. }
  727. /**
  728. * deletes the exercise from the database
  729. * Notice : leaves the question in the data base
  730. *
  731. * @author - Olivier Brouckaert
  732. */
  733. function delete(){
  734. global $_course,$_user;
  735. $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
  736. $sql="UPDATE $TBL_EXERCICES SET active='-1' WHERE id='".Database::escape_string($this->id)."'";
  737. api_sql_query($sql);
  738. api_item_property_update($_course, TOOL_QUIZ, $this->id,'QuizDeleted',$_user['user_id']);
  739. if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian') ) {
  740. $this -> search_engine_delete();
  741. }
  742. }
  743. /**
  744. * Creates the form to create / edit an exercise
  745. * @param FormValidator $form the formvalidator instance (by reference)
  746. */
  747. function createForm ($form, $type='full')
  748. {
  749. global $id;
  750. if(empty($type)){
  751. $type='full';
  752. }
  753. // form title
  754. if (!empty($_GET['exerciseId'])) {
  755. $form_title = get_lang('ModifyExercise');
  756. } else {
  757. $form_title = get_lang('NewEx');
  758. }
  759. $form->addElement('header', '', $form_title);
  760. // title
  761. $form -> addElement('text', 'exerciseTitle', get_lang('ExerciseName'),'class="input_titles"');
  762. //$form->applyFilter('exerciseTitle','html_filter');
  763. $form -> addElement('html','<div class="row">
  764. <div class="label"></div>
  765. <div class="formw" style="height:50px">
  766. <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>
  767. </div>
  768. </div>');
  769. $editor_config = array('ToolbarSet' => 'TestQuestionDescription', 'Width' => '100%', 'Height' => '150');
  770. if(is_array($type)){
  771. $editor_config = array_merge($editor_config, $type);
  772. }
  773. $form -> addElement ('html','<div class="HideFCKEditor" id="HiddenFCKexerciseDescription" >');
  774. $form -> add_html_editor('exerciseDescription', get_lang('langExerciseDescription'), false, false, $editor_config);
  775. $form -> addElement ('html','</div>');
  776. $form -> addElement('html','<div class="row">
  777. <div class="label">&nbsp;</div>
  778. <div class="formw">
  779. <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>
  780. </div>
  781. </div>');
  782. // Random questions
  783. $form -> addElement('html','<div id="options" style="display:none">');
  784. if($type=='full') {
  785. // feedback type
  786. $radios_feedback = array();
  787. $radios_feedback[] = FormValidator :: createElement ('radio', 'exerciseFeedbackType', null, get_lang('ExerciseAtTheEndOfTheTest'),'0');
  788. $radios_feedback[] = FormValidator :: createElement ('radio', 'exerciseFeedbackType', null, get_lang('NoFeedback'),'2');
  789. $form -> addGroup($radios_feedback, null, get_lang('FeedbackType'));
  790. $feedback_option[0]=get_lang('ExerciseAtTheEndOfTheTest');
  791. $feedback_option[1]=get_lang('DirectFeedback');
  792. $feedback_option[2]=get_lang('NoFeedback');
  793. //Can't modify a DirectFeedback question
  794. if ($this->selectFeedbackType() != 1 ) {
  795. // $form -> addElement('select', 'exerciseFeedbackType',get_lang('FeedbackType'),$feedback_option,'onchange="javascript:feedbackselection()"');
  796. // test type
  797. $radios = array();
  798. $radios[] = FormValidator :: createElement ('radio', 'exerciseType', null, get_lang('QuestionsPerPageOne'),'2');
  799. $radios[] = FormValidator :: createElement ('radio', 'exerciseType', null, get_lang('QuestionsPerPageAll'),'1');
  800. $form -> addGroup($radios, null, get_lang('QuestionsPerPage'));
  801. } else {
  802. // if is Directfeedback but has not questions we can allow to modify the question type
  803. if ($this->selectNbrQuestions()== 0) {
  804. $form -> addElement('select', 'exerciseFeedbackType',get_lang('FeedbackType'),$feedback_option,'onchange="javascript:feedbackselection()"');
  805. // test type
  806. $radios = array();
  807. $radios[] = FormValidator :: createElement ('radio', 'exerciseType', null, get_lang('SimpleExercise'),'1');
  808. $radios[] = FormValidator :: createElement ('radio', 'exerciseType', null, get_lang('SequentialExercise'),'2');
  809. $form -> addGroup($radios, null, get_lang('ExerciseType'));
  810. } else {
  811. //we force the options to the DirectFeedback exercisetype
  812. $form -> addElement('hidden', 'exerciseFeedbackType','1');
  813. $form -> addElement('hidden', 'exerciseType','2');
  814. }
  815. }
  816. $radios_results_disabled = array();
  817. $radios_results_disabled[] = FormValidator :: createElement ('radio', 'results_disabled', null, get_lang('Yes'),'0');
  818. $radios_results_disabled[] = FormValidator :: createElement ('radio', 'results_disabled', null, get_lang('No'),'1');
  819. $form -> addGroup($radios_results_disabled, null, get_lang('ShowResultsToStudents'));
  820. $random = array();
  821. $option=array();
  822. $max = ($this->id > 0) ? $this->selectNbrQuestions() : 10 ;
  823. $option = range(0,$max);
  824. $option[0]=get_lang('No');
  825. $random[] = FormValidator :: createElement ('select', 'randomQuestions',null,$option);
  826. $random[] = FormValidator :: createElement ('static', 'help','help','<span style="font-style: italic;">'.get_lang('RandomQuestionsHelp').'</span>');
  827. //$random[] = FormValidator :: createElement ('text', 'randomQuestions', null,null,'0');
  828. $form -> addGroup($random,null,get_lang('RandomQuestions'),'<br />');
  829. $attempt_option=range(0,10);
  830. $attempt_option[0]=get_lang('Infinite');
  831. $form -> addElement('select', 'exerciseAttempts',get_lang('ExerciseAttempts'),$attempt_option);
  832. $form -> addElement('checkbox', 'enabletimelimit',get_lang('EnableTimeLimits'),null,'onclick = " return timelimit() "');
  833. $var= Exercise::selectTimeLimit();
  834. $form -> addElement('html','</div>');
  835. if(($this -> start_time!='0000-00-00 00:00:00')||($this -> end_time!='0000-00-00 00:00:00'))
  836. $form -> addElement('html','<div id="options2" style="display:block;">');
  837. else
  838. $form -> addElement('html','<div id="options2" style="display:none;">');
  839. //$form -> addElement('date', 'start_time', get_lang('ExeStartTime'), array('language'=>'es','format' => 'dMYHi'));
  840. //$form -> addElement('date', 'end_time', get_lang('ExeEndTime'), array('language'=>'es','format' => 'dMYHi'));
  841. $form->addElement('datepicker', 'start_time', get_lang('ExeStartTime'), array('form_name'=>'exercise_admin'));
  842. $form->addElement('datepicker', 'end_time', get_lang('ExeEndTime'), array('form_name'=>'exercise_admin'));
  843. //$form -> addElement('text', 'exerciseAttempts', get_lang('ExerciseAttempts').' : ',array('size'=>'2'));
  844. $form -> addElement('html','</div>');
  845. $defaults = array();
  846. if (api_get_setting('search_enabled') === 'true') {
  847. require_once(api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php');
  848. $form -> addElement ('checkbox', 'index_document','', get_lang('SearchFeatureDoIndexDocument'));
  849. $form -> addElement ('html','<br /><div class="row">');
  850. $form -> addElement ('html', '<div class="label">'. get_lang('SearchFeatureDocumentLanguage') .'</div>');
  851. $form -> addElement ('html', '<div class="formw">'. api_get_languages_combo() .'</div>');
  852. $form -> addElement ('html','</div><div class="sub-form">');
  853. $specific_fields = get_specific_field_list();
  854. foreach ($specific_fields as $specific_field) {
  855. $form -> addElement ('text', $specific_field['code'], $specific_field['name']);
  856. $filter = array('course_code'=> "'". api_get_course_id() ."'", 'field_id' => $specific_field['id'], 'ref_id' => $this->id, 'tool_id' => '\''. TOOL_QUIZ .'\'');
  857. $values = get_specific_field_values_list($filter, array('value'));
  858. if ( !empty($values) ) {
  859. $arr_str_values = array();
  860. foreach ($values as $value) {
  861. $arr_str_values[] = $value['value'];
  862. }
  863. $defaults[$specific_field['code']] = implode(', ', $arr_str_values);
  864. }
  865. }
  866. $form -> addElement ('html','</div>');
  867. }
  868. }
  869. // submit
  870. isset($_GET['exerciseId'])?$text=get_lang('ModifyExercise'):$text=get_lang('ProcedToQuestions');
  871. $form -> addElement('html', '<br /><br />');
  872. $form -> addElement('style_submit_button', 'submitExercise', $text, 'class="save"');
  873. $form -> addRule ('exerciseTitle', get_lang('GiveExerciseName'), 'required');
  874. if($type=='full') {
  875. // rules
  876. $form -> addRule ('exerciseAttempts', get_lang('Numeric'), 'numeric');
  877. $form -> addRule ('start_time', get_lang('InvalidDate'), 'date');
  878. $form -> addRule ('end_time', get_lang('InvalidDate'), 'date');
  879. $form -> addRule(array ('start_time', 'end_time'), get_lang('StartDateShouldBeBeforeEndDate'), 'date_compare', 'lte');
  880. }
  881. // defaults
  882. if($type=='full') {
  883. if($this -> id > 0) {
  884. if ($this -> random > $this->selectNbrQuestions()) {
  885. $defaults['randomQuestions'] = $this->selectNbrQuestions();
  886. } else {
  887. $defaults['randomQuestions'] = $this -> random;
  888. }
  889. $defaults['exerciseType'] = $this -> selectType();
  890. $defaults['exerciseTitle'] = $this -> selectTitle();
  891. $defaults['exerciseDescription'] = $this -> selectDescription();
  892. $defaults['exerciseAttempts'] = $this->selectAttempts();
  893. $defaults['exerciseFeedbackType'] = $this->selectFeedbackType();
  894. $defaults['results_disabled'] = $this->selectResultsDisabled();
  895. if(($this -> start_time!='0000-00-00 00:00:00')||($this -> end_time!='0000-00-00 00:00:00'))
  896. $defaults['enabletimelimit'] = 1;
  897. $defaults['start_time'] = ($this->start_time!='0000-00-00 00:00:00')? $this -> start_time : date('Y-m-d 12:00:00');
  898. $defaults['end_time'] = ($this->end_time!='0000-00-00 00:00:00')?$this -> end_time : date('Y-m-d 12:00:00',time()+84600);
  899. } else {
  900. $defaults['exerciseType'] = 2;
  901. $defaults['exerciseAttempts'] = 0;
  902. $defaults['randomQuestions'] = 0;
  903. $defaults['exerciseDescription'] = '';
  904. $defaults['exerciseFeedbackType'] = 0;
  905. $defaults['results_disabled'] = 0;
  906. $defaults['start_time'] = date('Y-m-d 12:00:00');
  907. $defaults['end_time'] = date('Y-m-d 12:00:00',time()+84600);
  908. }
  909. } else {
  910. $defaults['exerciseTitle'] = $this -> selectTitle();
  911. $defaults['exerciseDescription'] = $this -> selectDescription();
  912. }
  913. if (api_get_setting('search_enabled') === 'true') {
  914. $defaults['index_document'] = 'checked="checked"';
  915. }
  916. $form -> setDefaults($defaults);
  917. }
  918. /**
  919. * function which process the creation of exercises
  920. * @param FormValidator $form the formvalidator instance
  921. */
  922. function processCreation($form,$type='')
  923. {
  924. $this -> updateTitle($form -> getSubmitValue('exerciseTitle'));
  925. $this -> updateDescription($form -> getSubmitValue('exerciseDescription'));
  926. $this -> updateAttempts($form -> getSubmitValue('exerciseAttempts'));
  927. $this -> updateFeedbackType($form -> getSubmitValue('exerciseFeedbackType'));
  928. $this -> updateType($form -> getSubmitValue('exerciseType'));
  929. $this -> setRandom($form -> getSubmitValue('randomQuestions'));
  930. $this -> updateResultsDisabled($form -> getSubmitValue('results_disabled'));
  931. if($form -> getSubmitValue('enabletimelimit')==1)
  932. {
  933. $start_time = $form -> getSubmitValue('start_time');
  934. $this->start_time = $start_time['Y'].'-'.$start_time['F'].'-'.$start_time['d'].' '.$start_time['H'].':'.$start_time['i'].':00';
  935. $end_time = $form -> getSubmitValue('end_time');
  936. $this->end_time = $end_time['Y'].'-'.$end_time['F'].'-'.$end_time['d'].' '.$end_time['H'].':'.$end_time['i'].':00';
  937. }
  938. else
  939. {
  940. $this->start_time = '0000-00-00 00:00:00';
  941. $this->end_time = '0000-00-00 00:00:00';
  942. }
  943. //echo $end_time;exit;
  944. $this -> save($type);
  945. }
  946. function search_engine_save() {
  947. if ($_POST['index_document'] != 1) {
  948. return;
  949. }
  950. $course_id = api_get_course_id();
  951. require_once(api_get_path(LIBRARY_PATH) . 'search/DokeosIndexer.class.php');
  952. require_once(api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php');
  953. require_once(api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php');
  954. $specific_fields = get_specific_field_list();
  955. $ic_slide = new IndexableChunk();
  956. $all_specific_terms = '';
  957. foreach ($specific_fields as $specific_field) {
  958. if (isset($_REQUEST[$specific_field['code']])) {
  959. $sterms = trim($_REQUEST[$specific_field['code']]);
  960. if (!empty($sterms)) {
  961. $all_specific_terms .= ' '. $sterms;
  962. $sterms = explode(',', $sterms);
  963. foreach ($sterms as $sterm) {
  964. $ic_slide->addTerm(trim($sterm), $specific_field['code']);
  965. add_specific_field_value($specific_field['id'], $course_id, TOOL_QUIZ, $this->id, $sterm);
  966. }
  967. }
  968. }
  969. }
  970. // build the chunk to index
  971. $ic_slide->addValue("title", $this->exercise);
  972. $ic_slide->addCourseId($course_id);
  973. $ic_slide->addToolId(TOOL_QUIZ);
  974. $xapian_data = array(
  975. SE_COURSE_ID => $course_id,
  976. SE_TOOL_ID => TOOL_QUIZ,
  977. SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_EXERCISE, 'exercise_id' => (int)$this->id),
  978. SE_USER => (int)api_get_user_id(),
  979. );
  980. $ic_slide->xapian_data = serialize($xapian_data);
  981. $exercise_description = $all_specific_terms .' '. $this->description;
  982. $ic_slide->addValue("content", $exercise_description);
  983. $di = new DokeosIndexer();
  984. isset($_POST['language'])? $lang=Database::escape_string($_POST['language']): $lang = 'english';
  985. $di->connectDb(NULL, NULL, $lang);
  986. $di->addChunk($ic_slide);
  987. //index and return search engine document id
  988. $did = $di->index();
  989. if ($did) {
  990. // save it to db
  991. $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  992. $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
  993. VALUES (NULL , \'%s\', \'%s\', %s, %s)';
  994. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id, $did);
  995. api_sql_query($sql,__FILE__,__LINE__);
  996. }
  997. }
  998. function search_engine_edit() {
  999. // update search enchine and its values table if enabled
  1000. if (api_get_setting('search_enabled')=='true' && extension_loaded('xapian')) {
  1001. $course_id = api_get_course_id();
  1002. // actually, it consists on delete terms from db, insert new ones, create a new search engine document, and remove the old one
  1003. // get search_did
  1004. $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  1005. $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s LIMIT 1';
  1006. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1007. $res = api_sql_query($sql, __FILE__, __LINE__);
  1008. if (Database::num_rows($res) > 0) {
  1009. require_once(api_get_path(LIBRARY_PATH) . 'search/DokeosIndexer.class.php');
  1010. require_once(api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php');
  1011. require_once(api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php');
  1012. $se_ref = Database::fetch_array($res);
  1013. $specific_fields = get_specific_field_list();
  1014. $ic_slide = new IndexableChunk();
  1015. $all_specific_terms = '';
  1016. foreach ($specific_fields as $specific_field) {
  1017. delete_all_specific_field_value($course_id, $specific_field['id'], TOOL_QUIZ, $this->id);
  1018. if (isset($_REQUEST[$specific_field['code']])) {
  1019. $sterms = trim($_REQUEST[$specific_field['code']]);
  1020. $all_specific_terms .= ' '. $sterms;
  1021. $sterms = explode(',', $sterms);
  1022. foreach ($sterms as $sterm) {
  1023. $ic_slide->addTerm(trim($sterm), $specific_field['code']);
  1024. add_specific_field_value($specific_field['id'], $course_id, TOOL_QUIZ, $this->id, $sterm);
  1025. }
  1026. }
  1027. }
  1028. // build the chunk to index
  1029. $ic_slide->addValue("title", $this->exercise);
  1030. $ic_slide->addCourseId($course_id);
  1031. $ic_slide->addToolId(TOOL_QUIZ);
  1032. $xapian_data = array(
  1033. SE_COURSE_ID => $course_id,
  1034. SE_TOOL_ID => TOOL_QUIZ,
  1035. SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_EXERCISE, 'exercise_id' => (int)$this->id),
  1036. SE_USER => (int)api_get_user_id(),
  1037. );
  1038. $ic_slide->xapian_data = serialize($xapian_data);
  1039. $exercise_description = $all_specific_terms .' '. $this->description;
  1040. $ic_slide->addValue("content", $exercise_description);
  1041. $di = new DokeosIndexer();
  1042. isset($_POST['language'])? $lang=Database::escape_string($_POST['language']): $lang = 'english';
  1043. $di->connectDb(NULL, NULL, $lang);
  1044. $di->remove_document((int)$se_ref['search_did']);
  1045. $di->addChunk($ic_slide);
  1046. //index and return search engine document id
  1047. $did = $di->index();
  1048. if ($did) {
  1049. // save it to db
  1050. $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=\'%s\'';
  1051. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1052. api_sql_query($sql,__FILE__,__LINE__);
  1053. //var_dump($sql);
  1054. $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, search_did)
  1055. VALUES (NULL , \'%s\', \'%s\', %s, %s)';
  1056. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id, $did);
  1057. api_sql_query($sql,__FILE__,__LINE__);
  1058. }
  1059. }
  1060. }
  1061. }
  1062. function search_engine_delete() {
  1063. // remove from search engine if enabled
  1064. if (api_get_setting('search_enabled') == 'true' && extension_loaded('xapian') ) {
  1065. $course_id = api_get_course_id();
  1066. $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  1067. $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';
  1068. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1069. $res = api_sql_query($sql, __FILE__, __LINE__);
  1070. if (Database::num_rows($res) > 0) {
  1071. $row = Database::fetch_array($res);
  1072. require_once(api_get_path(LIBRARY_PATH) .'search/DokeosIndexer.class.php');
  1073. $di = new DokeosIndexer();
  1074. $di->remove_document((int)$row['search_did']);
  1075. unset($di);
  1076. $tbl_quiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
  1077. foreach ( $this->questionList as $question_i) {
  1078. $sql = 'SELECT type FROM %s WHERE id=%s';
  1079. $sql = sprintf($sql, $tbl_quiz_question, $question_i);
  1080. $qres = api_sql_query($sql, __FILE__, __LINE__);
  1081. if (Database::num_rows($qres) > 0) {
  1082. $qrow = Database::fetch_array($qres);
  1083. $objQuestion = Question::getInstance($qrow['type']);
  1084. $objQuestion = Question::read((int)$question_i);
  1085. $objQuestion->search_engine_edit($this->id, FALSE, TRUE);
  1086. unset($objQuestion);
  1087. }
  1088. }
  1089. }
  1090. $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';
  1091. $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id);
  1092. api_sql_query($sql, __FILE__, __LINE__);
  1093. // remove terms from db
  1094. require_once(api_get_path(LIBRARY_PATH) .'specific_fields_manager.lib.php');
  1095. delete_all_values_for_item($course_id, TOOL_QUIZ, $this->id);
  1096. }
  1097. }
  1098. }
  1099. endif;
  1100. ?>