answer.class.php 34 KB

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