answer.class.php 34 KB

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