answer.class.php 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use Chamilo\CourseBundle\Entity\CQuizAnswer;
  4. /**
  5. * Class Answer
  6. * Allows to instantiate an object of type Answer
  7. * 5 arrays are created to receive the attributes of each answer belonging to a specified question.
  8. *
  9. * @package chamilo.exercise
  10. *
  11. * @author Olivier Brouckaert
  12. */
  13. class Answer
  14. {
  15. public $questionId;
  16. // these are arrays
  17. public $answer;
  18. public $correct;
  19. public $comment;
  20. public $weighting;
  21. public $position;
  22. public $hotspot_coordinates;
  23. public $hotspot_type;
  24. public $destination;
  25. // these arrays are used to save temporarily new answers
  26. // then they are moved into the arrays above or deleted in the event of cancellation
  27. public $new_answer;
  28. public $new_correct;
  29. public $new_comment;
  30. public $new_weighting;
  31. public $new_position;
  32. public $new_hotspot_coordinates;
  33. public $new_hotspot_type;
  34. public $autoId;
  35. public $nbrAnswers;
  36. public $new_nbrAnswers;
  37. public $new_destination; // id of the next question if feedback option is set to Directfeedback
  38. public $course; //Course information
  39. public $iid;
  40. public $questionJSId;
  41. public $standalone;
  42. /**
  43. * constructor of the class.
  44. *
  45. * @author Olivier Brouckaert
  46. *
  47. * @param int $questionId that answers belong to
  48. * @param int $course_id
  49. * @param Exercise $exercise
  50. */
  51. public function __construct($questionId, $course_id = 0, $exercise = null)
  52. {
  53. $this->questionId = intval($questionId);
  54. $this->answer = [];
  55. $this->correct = [];
  56. $this->comment = [];
  57. $this->weighting = [];
  58. $this->position = [];
  59. $this->hotspot_coordinates = [];
  60. $this->hotspot_type = [];
  61. $this->destination = [];
  62. // clears $new_* arrays
  63. $this->cancel();
  64. if (!empty($course_id)) {
  65. $courseInfo = api_get_course_info_by_id($course_id);
  66. } else {
  67. $courseInfo = api_get_course_info();
  68. }
  69. $this->course = $courseInfo;
  70. $this->course_id = $courseInfo['real_id'];
  71. if (empty($exercise)) {
  72. // fills arrays
  73. $objExercise = new Exercise($this->course_id);
  74. $exerciseId = isset($_REQUEST['exerciseId']) ? $_REQUEST['exerciseId'] : null;
  75. $objExercise->read($exerciseId);
  76. } else {
  77. $objExercise = $exercise;
  78. }
  79. if ($objExercise->random_answers == '1' && $this->getQuestionType() != CALCULATED_ANSWER) {
  80. $this->readOrderedBy('rand()', ''); // randomize answers
  81. } else {
  82. $this->read(); // natural order
  83. }
  84. }
  85. /**
  86. * Clears $new_* arrays.
  87. *
  88. * @author Olivier Brouckaert
  89. */
  90. public function cancel()
  91. {
  92. $this->new_answer = [];
  93. $this->new_correct = [];
  94. $this->new_comment = [];
  95. $this->new_weighting = [];
  96. $this->new_position = [];
  97. $this->new_hotspot_coordinates = [];
  98. $this->new_hotspot_type = [];
  99. $this->new_nbrAnswers = 0;
  100. $this->new_destination = [];
  101. }
  102. /**
  103. * Reads answer information from the database.
  104. *
  105. * @author Olivier Brouckaert
  106. */
  107. public function read()
  108. {
  109. $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
  110. $questionId = $this->questionId;
  111. $sql = "SELECT * FROM $TBL_ANSWER
  112. WHERE
  113. c_id = {$this->course_id} AND
  114. question_id ='".$questionId."'
  115. ORDER BY position";
  116. $result = Database::query($sql);
  117. $i = 1;
  118. // while a record is found
  119. while ($object = Database::fetch_object($result)) {
  120. $this->id[$i] = $object->id;
  121. $this->answer[$i] = $object->answer;
  122. $this->correct[$i] = $object->correct;
  123. $this->comment[$i] = $object->comment;
  124. $this->weighting[$i] = $object->ponderation;
  125. $this->position[$i] = $object->position;
  126. $this->hotspot_coordinates[$i] = $object->hotspot_coordinates;
  127. $this->hotspot_type[$i] = $object->hotspot_type;
  128. $this->destination[$i] = $object->destination;
  129. $this->autoId[$i] = $object->id_auto;
  130. $this->iid[$i] = $object->iid;
  131. $i++;
  132. }
  133. $this->nbrAnswers = $i - 1;
  134. }
  135. /**
  136. * @param int $id
  137. *
  138. * @return array
  139. */
  140. public function getAnswerByAutoId($id)
  141. {
  142. foreach ($this->autoId as $key => $autoId) {
  143. if ($autoId == $id) {
  144. $result = [
  145. 'answer' => $this->answer[$key],
  146. 'correct' => $this->correct[$key],
  147. 'comment' => $this->comment[$key],
  148. ];
  149. return $result;
  150. }
  151. }
  152. return [];
  153. }
  154. /**
  155. * returns all answer ids from this question Id.
  156. *
  157. * @author Yoselyn Castillo
  158. *
  159. * @return array - $id (answer ids)
  160. */
  161. public function selectAnswerId()
  162. {
  163. $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
  164. $questionId = $this->questionId;
  165. $sql = "SELECT id FROM
  166. $TBL_ANSWER
  167. WHERE c_id = {$this->course_id} AND question_id ='".$questionId."'";
  168. $result = Database::query($sql);
  169. $id = [];
  170. // while a record is found
  171. if (Database::num_rows($result) > 0) {
  172. while ($object = Database::fetch_array($result)) {
  173. $id[] = $object['id'];
  174. }
  175. }
  176. return $id;
  177. }
  178. /**
  179. * Reads answer information from the data base ordered by parameter.
  180. *
  181. * @param string $field Field we want to order by
  182. * @param string $order DESC or ASC
  183. *
  184. * @return bool
  185. *
  186. * @author Frederic Vauthier
  187. */
  188. public function readOrderedBy($field, $order = 'ASC')
  189. {
  190. $field = Database::escape_string($field);
  191. if (empty($field)) {
  192. $field = 'position';
  193. }
  194. if ($order != 'ASC' && $order != 'DESC') {
  195. $order = 'ASC';
  196. }
  197. $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
  198. $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_QUESTION);
  199. $questionId = intval($this->questionId);
  200. $sql = "SELECT type FROM $TBL_QUIZ
  201. WHERE c_id = {$this->course_id} AND id = $questionId";
  202. $result_question = Database::query($sql);
  203. $questionType = Database::fetch_array($result_question);
  204. if ($questionType['type'] == DRAGGABLE) {
  205. // Random is done by submit.js.tpl
  206. $this->read();
  207. return true;
  208. }
  209. $sql = "SELECT
  210. answer,
  211. correct,
  212. comment,
  213. ponderation,
  214. position,
  215. hotspot_coordinates,
  216. hotspot_type,
  217. destination,
  218. id_auto,
  219. iid
  220. FROM $TBL_ANSWER
  221. WHERE
  222. c_id = {$this->course_id} AND
  223. question_id='".$questionId."'
  224. ORDER BY $field $order";
  225. $result = Database::query($sql);
  226. $i = 1;
  227. // while a record is found
  228. $doubt_data = null;
  229. while ($object = Database::fetch_object($result)) {
  230. if ($questionType['type'] == UNIQUE_ANSWER_NO_OPTION && $object->position == 666) {
  231. $doubt_data = $object;
  232. continue;
  233. }
  234. $this->answer[$i] = $object->answer;
  235. $this->correct[$i] = $object->correct;
  236. $this->comment[$i] = $object->comment;
  237. $this->weighting[$i] = $object->ponderation;
  238. $this->position[$i] = $object->position;
  239. $this->hotspot_coordinates[$i] = $object->hotspot_coordinates;
  240. $this->hotspot_type[$i] = $object->hotspot_type;
  241. $this->destination[$i] = $object->destination;
  242. $this->autoId[$i] = $object->id_auto;
  243. $this->iid[$i] = $object->iid;
  244. $i++;
  245. }
  246. if ($questionType['type'] == UNIQUE_ANSWER_NO_OPTION && !empty($doubt_data)) {
  247. $this->answer[$i] = $doubt_data->answer;
  248. $this->correct[$i] = $doubt_data->correct;
  249. $this->comment[$i] = $doubt_data->comment;
  250. $this->weighting[$i] = $doubt_data->ponderation;
  251. $this->position[$i] = $doubt_data->position;
  252. $this->hotspot_coordinates[$i] = isset($object->hotspot_coordinates) ? $object->hotspot_coordinates : 0;
  253. $this->hotspot_type[$i] = isset($object->hotspot_type) ? $object->hotspot_type : 0;
  254. $this->destination[$i] = $doubt_data->destination;
  255. $this->autoId[$i] = $doubt_data->id_auto;
  256. $this->iid[$i] = $doubt_data->iid;
  257. $i++;
  258. }
  259. $this->nbrAnswers = $i - 1;
  260. return true;
  261. }
  262. /**
  263. * returns the autoincrement id.
  264. *
  265. * @author Juan Carlos Ra�a
  266. *
  267. * @return int - answer num
  268. */
  269. public function selectAutoId($id)
  270. {
  271. return isset($this->autoId[$id]) ? $this->autoId[$id] : 0;
  272. }
  273. /**
  274. * returns the number of answers in this question.
  275. *
  276. * @author Olivier Brouckaert
  277. *
  278. * @return int - number of answers
  279. */
  280. public function selectNbrAnswers()
  281. {
  282. return $this->nbrAnswers;
  283. }
  284. /**
  285. * returns the question ID which the answers belong to.
  286. *
  287. * @author Olivier Brouckaert
  288. *
  289. * @return int - the question ID
  290. */
  291. public function selectQuestionId()
  292. {
  293. return $this->questionId;
  294. }
  295. /**
  296. * returns the question ID of the destination question.
  297. *
  298. * @author Julio Montoya
  299. *
  300. * @param int $id
  301. *
  302. * @return int - the question ID
  303. */
  304. public function selectDestination($id)
  305. {
  306. return isset($this->destination[$id]) ? $this->destination[$id] : null;
  307. }
  308. /**
  309. * returns the answer title.
  310. *
  311. * @author Olivier Brouckaert
  312. *
  313. * @param - integer $id - answer ID
  314. *
  315. * @return string - answer title
  316. */
  317. public function selectAnswer($id)
  318. {
  319. return isset($this->answer[$id]) ? $this->answer[$id] : null;
  320. }
  321. /**
  322. * return array answer by id else return a bool.
  323. *
  324. * @param int $auto_id
  325. *
  326. * @return array
  327. */
  328. public function selectAnswerByAutoId($auto_id)
  329. {
  330. $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
  331. $auto_id = intval($auto_id);
  332. $sql = "SELECT id, answer, id_auto FROM $TBL_ANSWER
  333. WHERE c_id = {$this->course_id} AND id_auto='$auto_id'";
  334. $rs = Database::query($sql);
  335. if (Database::num_rows($rs) > 0) {
  336. $row = Database::fetch_array($rs, 'ASSOC');
  337. return $row;
  338. }
  339. return false;
  340. }
  341. /**
  342. * returns the answer title from an answer's position.
  343. *
  344. * @author Yannick Warnier
  345. *
  346. * @param - integer $id - answer ID
  347. *
  348. * @return bool - answer title
  349. */
  350. public function selectAnswerIdByPosition($pos)
  351. {
  352. foreach ($this->position as $k => $v) {
  353. if ($v != $pos) {
  354. continue;
  355. }
  356. return $k;
  357. }
  358. return false;
  359. }
  360. /**
  361. * Returns a list of answers.
  362. *
  363. * @author Yannick Warnier <ywarnier@beeznest.org>
  364. *
  365. * @param bool $decode
  366. *
  367. * @return array List of answers where each answer is an array
  368. * of (id, answer, comment, grade) and grade=weighting
  369. */
  370. public function getAnswersList($decode = false)
  371. {
  372. $list = [];
  373. for ($i = 1; $i <= $this->nbrAnswers; $i++) {
  374. if (!empty($this->answer[$i])) {
  375. //Avoid problems when parsing elements with accents
  376. if ($decode) {
  377. $this->answer[$i] = api_html_entity_decode(
  378. $this->answer[$i],
  379. ENT_QUOTES,
  380. api_get_system_encoding()
  381. );
  382. $this->comment[$i] = api_html_entity_decode(
  383. $this->comment[$i],
  384. ENT_QUOTES,
  385. api_get_system_encoding()
  386. );
  387. }
  388. $list[] = [
  389. 'id' => $i,
  390. 'answer' => $this->answer[$i],
  391. 'comment' => $this->comment[$i],
  392. 'grade' => $this->weighting[$i],
  393. 'hotspot_coord' => $this->hotspot_coordinates[$i],
  394. 'hotspot_type' => $this->hotspot_type[$i],
  395. 'correct' => $this->correct[$i],
  396. 'destination' => $this->destination[$i],
  397. ];
  398. }
  399. }
  400. return $list;
  401. }
  402. /**
  403. * Returns a list of grades.
  404. *
  405. * @author Yannick Warnier <ywarnier@beeznest.org>
  406. *
  407. * @return array List of grades where grade=weighting (?)
  408. */
  409. public function getGradesList()
  410. {
  411. $list = [];
  412. for ($i = 0; $i < $this->nbrAnswers; $i++) {
  413. if (!empty($this->answer[$i])) {
  414. $list[$i] = $this->weighting[$i];
  415. }
  416. }
  417. return $list;
  418. }
  419. /**
  420. * Returns the question type.
  421. *
  422. * @author Yannick Warnier <ywarnier@beeznest.org>
  423. *
  424. * @return int The type of the question this answer is bound to
  425. */
  426. public function getQuestionType()
  427. {
  428. $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
  429. $sql = "SELECT type FROM $TBL_QUESTIONS
  430. WHERE c_id = {$this->course_id} AND id = '".$this->questionId."'";
  431. $res = Database::query($sql);
  432. if (Database::num_rows($res) <= 0) {
  433. return null;
  434. }
  435. $row = Database::fetch_array($res);
  436. return $row['type'];
  437. }
  438. /**
  439. * tells if answer is correct or not.
  440. *
  441. * @author Olivier Brouckaert
  442. *
  443. * @param - integer $id - answer ID
  444. *
  445. * @return int - 0 if bad answer, not 0 if good answer
  446. */
  447. public function isCorrect($id)
  448. {
  449. return isset($this->correct[$id]) ? $this->correct[$id] : null;
  450. }
  451. /**
  452. * returns answer comment.
  453. *
  454. * @author Olivier Brouckaert
  455. *
  456. * @param - integer $id - answer ID
  457. *
  458. * @return string - answer comment
  459. */
  460. public function selectComment($id)
  461. {
  462. return isset($this->comment[$id]) ? $this->comment[$id] : null;
  463. }
  464. /**
  465. * returns answer weighting.
  466. *
  467. * @author Olivier Brouckaert
  468. *
  469. * @param - integer $id - answer ID
  470. *
  471. * @return int - answer weighting
  472. */
  473. public function selectWeighting($id)
  474. {
  475. return isset($this->weighting[$id]) ? $this->weighting[$id] : null;
  476. }
  477. /**
  478. * returns answer position.
  479. *
  480. * @author Olivier Brouckaert
  481. *
  482. * @param - integer $id - answer ID
  483. *
  484. * @return int - answer position
  485. */
  486. public function selectPosition($id)
  487. {
  488. return isset($this->position[$id]) ? $this->position[$id] : null;
  489. }
  490. /**
  491. * returns answer hotspot coordinates.
  492. *
  493. * @author Olivier Brouckaert
  494. *
  495. * @param int $id Answer ID
  496. *
  497. * @return int Answer position
  498. */
  499. public function selectHotspotCoordinates($id)
  500. {
  501. return isset($this->hotspot_coordinates[$id]) ? $this->hotspot_coordinates[$id] : null;
  502. }
  503. /**
  504. * returns answer hotspot type.
  505. *
  506. * @author Toon Keppens
  507. *
  508. * @param int $id Answer ID
  509. *
  510. * @return int Answer position
  511. */
  512. public function selectHotspotType($id)
  513. {
  514. return isset($this->hotspot_type[$id]) ? $this->hotspot_type[$id] : null;
  515. }
  516. /**
  517. * Creates a new answer.
  518. *
  519. * @author Olivier Brouckaert
  520. *
  521. * @param string $answer answer title
  522. * @param int $correct 0 if bad answer, not 0 if good answer
  523. * @param string $comment answer comment
  524. * @param int $weighting answer weighting
  525. * @param int $position answer position
  526. * @param array $new_hotspot_coordinates Coordinates for hotspot exercises (optional)
  527. * @param int $new_hotspot_type Type for hotspot exercises (optional)
  528. * @param string $destination
  529. */
  530. public function createAnswer(
  531. $answer,
  532. $correct,
  533. $comment,
  534. $weighting,
  535. $position,
  536. $new_hotspot_coordinates = null,
  537. $new_hotspot_type = null,
  538. $destination = ''
  539. ) {
  540. $this->new_nbrAnswers++;
  541. $id = $this->new_nbrAnswers;
  542. $this->new_answer[$id] = $answer;
  543. $this->new_correct[$id] = $correct;
  544. $this->new_comment[$id] = $comment;
  545. $this->new_weighting[$id] = $weighting;
  546. $this->new_position[$id] = $position;
  547. $this->new_hotspot_coordinates[$id] = $new_hotspot_coordinates;
  548. $this->new_hotspot_type[$id] = $new_hotspot_type;
  549. $this->new_destination[$id] = $destination;
  550. }
  551. /**
  552. * Updates an answer.
  553. *
  554. * @author Toon Keppens
  555. *
  556. * @param int $iid
  557. * @param string $answer
  558. * @param string $comment
  559. * @param string $correct
  560. * @param string $weighting
  561. * @param string $position
  562. * @param string $destination
  563. * @param string $hotSpotCoordinates
  564. * @param string $hotSpotType
  565. *
  566. * @return CQuizAnswer
  567. */
  568. public function updateAnswers(
  569. $iid,
  570. $answer,
  571. $comment,
  572. $correct,
  573. $weighting,
  574. $position,
  575. $destination,
  576. $hotSpotCoordinates,
  577. $hotSpotType
  578. ) {
  579. $em = Database::getManager();
  580. /** @var CQuizAnswer $quizAnswer */
  581. $quizAnswer = $em->find('ChamiloCourseBundle:CQuizAnswer', $iid);
  582. if ($quizAnswer) {
  583. $quizAnswer
  584. ->setAnswer($answer)
  585. ->setComment($comment)
  586. ->setCorrect($correct)
  587. ->setPonderation($weighting)
  588. ->setPosition($position)
  589. ->setDestination($destination)
  590. ->setHotspotCoordinates($hotSpotCoordinates)
  591. ->setHotspotType($hotSpotType);
  592. $em->merge($quizAnswer);
  593. $em->flush();
  594. return $quizAnswer;
  595. }
  596. return false;
  597. }
  598. /**
  599. * Records answers into the data base.
  600. *
  601. * @author Olivier Brouckaert
  602. */
  603. public function save()
  604. {
  605. $answerTable = Database::get_course_table(TABLE_QUIZ_ANSWER);
  606. $em = Database::getManager();
  607. $questionId = (int) $this->questionId;
  608. $courseId = $this->course['real_id'];
  609. $correctList = [];
  610. $answerList = [];
  611. for ($i = 1; $i <= $this->new_nbrAnswers; $i++) {
  612. $answer = $this->new_answer[$i];
  613. $correct = isset($this->new_correct[$i]) ? $this->new_correct[$i] : '';
  614. $comment = isset($this->new_comment[$i]) ? $this->new_comment[$i] : '';
  615. $weighting = isset($this->new_weighting[$i]) ? $this->new_weighting[$i] : '';
  616. $position = isset($this->new_position[$i]) ? $this->new_position[$i] : '';
  617. $hotspot_coordinates = isset($this->new_hotspot_coordinates[$i]) ? $this->new_hotspot_coordinates[$i] : '';
  618. $hotspot_type = isset($this->new_hotspot_type[$i]) ? $this->new_hotspot_type[$i] : '';
  619. $destination = isset($this->new_destination[$i]) ? $this->new_destination[$i] : '';
  620. $autoId = $this->selectAutoId($i);
  621. $iid = isset($this->iid[$i]) ? $this->iid[$i] : 0;
  622. if (!isset($this->position[$i])) {
  623. $quizAnswer = new CQuizAnswer();
  624. $quizAnswer
  625. ->setIdAuto($autoId)
  626. ->setCId($courseId)
  627. ->setQuestionId($questionId)
  628. ->setAnswer($answer)
  629. ->setCorrect($correct)
  630. ->setComment($comment)
  631. ->setPonderation($weighting)
  632. ->setPosition($position)
  633. ->setHotspotCoordinates($hotspot_coordinates)
  634. ->setHotspotType($hotspot_type)
  635. ->setDestination($destination);
  636. $em->persist($quizAnswer);
  637. $em->flush();
  638. $iid = $quizAnswer->getIid();
  639. if ($iid) {
  640. $quizAnswer
  641. ->setId($iid)
  642. ->setIdAuto($iid);
  643. $em->merge($quizAnswer);
  644. $em->flush();
  645. $questionType = $this->getQuestionType();
  646. if (in_array(
  647. $questionType,
  648. [MATCHING, MATCHING_DRAGGABLE]
  649. )) {
  650. $answer = new Answer($this->questionId);
  651. $answer->read();
  652. $correctAnswerId = $answer->selectAnswerIdByPosition($correct);
  653. // Continue to avoid matching question bug if $correctAnswerId returns false
  654. // See : https://support.chamilo.org/issues/8334
  655. if ($questionType == MATCHING && !$correctAnswerId) {
  656. continue;
  657. }
  658. $correctAnswerAutoId = $answer->selectAutoId($correct);
  659. $quizAnswer->setCorrect($correctAnswerAutoId ? $correctAnswerAutoId : 0);
  660. $em->merge($quizAnswer);
  661. $em->flush();
  662. }
  663. }
  664. } else {
  665. // https://support.chamilo.org/issues/6558
  666. // function updateAnswers already escape_string, error if we do it twice.
  667. // Feed function updateAnswers with none escaped strings
  668. $this->updateAnswers(
  669. $iid,
  670. $this->new_answer[$i],
  671. $this->new_comment[$i],
  672. $this->new_correct[$i],
  673. $this->new_weighting[$i],
  674. $this->new_position[$i],
  675. $this->new_destination[$i],
  676. $this->new_hotspot_coordinates[$i],
  677. $this->new_hotspot_type[$i]
  678. );
  679. }
  680. $answerList[$i] = $iid;
  681. if ($correct) {
  682. $correctList[$iid] = true;
  683. }
  684. }
  685. $questionType = self::getQuestionType();
  686. switch ($questionType) {
  687. case MATCHING_DRAGGABLE:
  688. foreach ($this->new_correct as $value => $status) {
  689. if (!empty($status)) {
  690. if (isset($answerList[$status])) {
  691. $correct = $answerList[$status];
  692. } else {
  693. $correct = $status;
  694. }
  695. $myAutoId = $answerList[$value];
  696. $sql = "UPDATE $answerTable
  697. SET correct = '$correct'
  698. WHERE
  699. id_auto = $myAutoId
  700. ";
  701. Database::query($sql);
  702. }
  703. }
  704. break;
  705. case DRAGGABLE:
  706. foreach ($this->new_correct as $value => $status) {
  707. if (!empty($status)) {
  708. $correct = $answerList[$status];
  709. $myAutoId = $answerList[$value];
  710. $sql = "UPDATE $answerTable
  711. SET correct = '$correct'
  712. WHERE
  713. id_auto = $myAutoId
  714. ";
  715. Database::query($sql);
  716. }
  717. }
  718. break;
  719. }
  720. if (count($this->position) > $this->new_nbrAnswers) {
  721. $i = $this->new_nbrAnswers + 1;
  722. while ($this->position[$i]) {
  723. $position = $this->position[$i];
  724. $sql = "DELETE FROM $answerTable
  725. WHERE
  726. c_id = {$this->course_id} AND
  727. question_id = '".$questionId."' AND
  728. position ='$position'";
  729. Database::query($sql);
  730. $i++;
  731. }
  732. }
  733. // moves $new_* arrays
  734. $this->answer = $this->new_answer;
  735. $this->correct = $this->new_correct;
  736. $this->comment = $this->new_comment;
  737. $this->weighting = $this->new_weighting;
  738. $this->position = $this->new_position;
  739. $this->hotspot_coordinates = $this->new_hotspot_coordinates;
  740. $this->hotspot_type = $this->new_hotspot_type;
  741. $this->nbrAnswers = $this->new_nbrAnswers;
  742. $this->destination = $this->new_destination;
  743. $this->cancel();
  744. }
  745. /**
  746. * Duplicates answers by copying them into another question.
  747. *
  748. * @author Olivier Brouckaert
  749. *
  750. * @param Question $newQuestion
  751. * @param array $course_info destination course info (result of the function api_get_course_info() )
  752. */
  753. public function duplicate($newQuestion, $course_info = null)
  754. {
  755. $newQuestionId = $newQuestion->id;
  756. if (empty($course_info)) {
  757. $course_info = $this->course;
  758. }
  759. $fixed_list = [];
  760. $tableAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
  761. if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE ||
  762. self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE
  763. ) {
  764. // Selecting origin options
  765. $origin_options = Question::readQuestionOption(
  766. $this->selectQuestionId(),
  767. $this->course['real_id']
  768. );
  769. if (!empty($origin_options)) {
  770. foreach ($origin_options as $item) {
  771. $new_option_list[] = $item['id'];
  772. }
  773. }
  774. $destination_options = Question::readQuestionOption(
  775. $newQuestionId,
  776. $course_info['real_id']
  777. );
  778. $i = 0;
  779. if (!empty($destination_options)) {
  780. foreach ($destination_options as $item) {
  781. $fixed_list[$new_option_list[$i]] = $item['id'];
  782. $i++;
  783. }
  784. }
  785. }
  786. // if at least one answer
  787. if ($this->nbrAnswers) {
  788. // inserts new answers into data base
  789. $courseId = $course_info['real_id'];
  790. $correctAnswers = [];
  791. $onlyAnswers = [];
  792. $allAnswers = [];
  793. $em = Database::getManager();
  794. if (in_array($newQuestion->type, [MATCHING, MATCHING_DRAGGABLE])) {
  795. $temp = [];
  796. for ($i = 1; $i <= $this->nbrAnswers; $i++) {
  797. $answer = [
  798. 'id' => $this->id[$i],
  799. 'answer' => $this->answer[$i],
  800. 'correct' => $this->correct[$i],
  801. 'comment' => $this->comment[$i],
  802. 'weighting' => $this->weighting[$i],
  803. 'ponderation' => $this->weighting[$i],
  804. 'position' => $this->position[$i],
  805. 'hotspot_coordinates' => $this->hotspot_coordinates[$i],
  806. 'hotspot_type' => $this->hotspot_type[$i],
  807. 'destination' => $this->destination[$i],
  808. ];
  809. $temp[$answer['position']] = $answer;
  810. $allAnswers[$this->id[$i]] = $this->answer[$i];
  811. }
  812. foreach ($temp as $index => $answer) {
  813. if ($this->course['id'] != $course_info['id']) {
  814. // check resources inside html from ckeditor tool and copy correct urls into recipient course
  815. $answer['answer'] = DocumentManager::replaceUrlWithNewCourseCode(
  816. $answer['answer'],
  817. $this->course['id'],
  818. $course_info['id']
  819. );
  820. $answer['comment'] = DocumentManager::replaceUrlWithNewCourseCode(
  821. $answer['comment'],
  822. $this->course['id'],
  823. $course_info['id']
  824. );
  825. }
  826. $quizAnswer = new CQuizAnswer();
  827. $quizAnswer
  828. ->setCId($courseId)
  829. ->setQuestionId($newQuestionId)
  830. ->setAnswer($answer['answer'])
  831. ->setCorrect($answer['correct'])
  832. ->setComment($answer['comment'])
  833. ->setPonderation($answer['ponderation'])
  834. ->setPosition($answer['position'])
  835. ->setHotspotCoordinates($answer['hotspot_coordinates'])
  836. ->setHotspotType($answer['hotspot_type'])
  837. ->setIdAuto(0);
  838. $em->persist($quizAnswer);
  839. $em->flush();
  840. $answerId = $quizAnswer->getIid();
  841. if ($answerId) {
  842. $quizAnswer
  843. ->setId($answerId)
  844. ->setIdAuto($answerId);
  845. $em->merge($quizAnswer);
  846. $em->flush();
  847. $correctAnswers[$answerId] = $answer['correct'];
  848. $onlyAnswers[$answerId] = $answer['answer'];
  849. }
  850. }
  851. } else {
  852. for ($i = 1; $i <= $this->nbrAnswers; $i++) {
  853. if ($this->course['id'] != $course_info['id']) {
  854. $this->answer[$i] = DocumentManager::replaceUrlWithNewCourseCode(
  855. $this->answer[$i],
  856. $this->course['id'],
  857. $course_info['id']
  858. );
  859. $this->comment[$i] = DocumentManager::replaceUrlWithNewCourseCode(
  860. $this->comment[$i],
  861. $this->course['id'],
  862. $course_info['id']
  863. );
  864. }
  865. $correct = $this->correct[$i];
  866. if ($newQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE ||
  867. $newQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE
  868. ) {
  869. $correct = $fixed_list[intval($correct)];
  870. }
  871. $quizAnswer = new CQuizAnswer();
  872. $quizAnswer
  873. ->setCId($courseId)
  874. ->setQuestionId($newQuestionId)
  875. ->setAnswer($this->answer[$i])
  876. ->setCorrect($correct)
  877. ->setComment($this->comment[$i])
  878. ->setPonderation($this->weighting[$i])
  879. ->setPosition($this->position[$i])
  880. ->setHotspotCoordinates($this->hotspot_coordinates[$i])
  881. ->setHotspotType($this->hotspot_type[$i])
  882. ->setDestination($this->destination[$i]);
  883. $em->persist($quizAnswer);
  884. $em->flush();
  885. $answerId = $quizAnswer->getIid();
  886. $quizAnswer
  887. ->setId($answerId)
  888. ->setIdAuto($answerId);
  889. $em->merge($quizAnswer);
  890. $em->flush();
  891. $correctAnswers[$answerId] = $correct;
  892. $onlyAnswers[$answerId] = $this->answer[$i];
  893. $allAnswers[$this->id[$i]] = $this->answer[$i];
  894. }
  895. }
  896. // Fix correct answers
  897. if (in_array($newQuestion->type, [DRAGGABLE, MATCHING, MATCHING_DRAGGABLE])) {
  898. $onlyAnswersFlip = array_flip($onlyAnswers);
  899. foreach ($correctAnswers as $answer_id => $correct_answer) {
  900. $params = [];
  901. if (isset($allAnswers[$correct_answer]) &&
  902. isset($onlyAnswersFlip[$allAnswers[$correct_answer]])
  903. ) {
  904. $params['correct'] = $onlyAnswersFlip[$allAnswers[$correct_answer]];
  905. Database::update(
  906. $tableAnswer,
  907. $params,
  908. [
  909. 'id = ? AND c_id = ? AND question_id = ? ' => [
  910. $answer_id,
  911. $courseId,
  912. $newQuestionId,
  913. ],
  914. ]
  915. );
  916. }
  917. }
  918. }
  919. }
  920. }
  921. /**
  922. * Get the necessary JavaScript for some answers.
  923. *
  924. * @return string
  925. */
  926. public function getJs()
  927. {
  928. //if ($this->questionId == 2)
  929. return "<script>
  930. jsPlumb.ready(function() {
  931. if ($('#drag{$this->questionId}_question').length > 0) {
  932. MatchingDraggable.init('{$this->questionId}');
  933. }
  934. });
  935. </script>";
  936. }
  937. /**
  938. * Check if a answer is correct by an answer auto id.
  939. *
  940. * @param $needle int The answer auto id
  941. *
  942. * @return bool
  943. */
  944. public function isCorrectByAutoId($needle)
  945. {
  946. $key = 0;
  947. if (is_array($this->autoId)) {
  948. foreach ($this->autoId as $autoIdKey => $autoId) {
  949. if ($autoId == $needle) {
  950. $key = $autoIdKey;
  951. }
  952. }
  953. }
  954. if (!$key) {
  955. return false;
  956. }
  957. return $this->isCorrect($key) ? true : false;
  958. }
  959. }