answer.class.php 32 KB

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