Answer::selectHotspotCoordinates()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Framework\Container;
6
use Chamilo\CourseBundle\Entity\CQuizAnswer;
7
use Chamilo\CourseBundle\Entity\CQuizQuestion;
8
9
/**
10
 * Class Answer
11
 * Allows to instantiate an object of type Answer
12
 * 5 arrays are created to receive the attributes of each answer belonging to a specified question.
13
 *
14
 * @author Olivier Brouckaert
15
 */
16
class Answer
17
{
18
    public $questionId;
19
20
    // these are arrays
21
    public $answer;
22
    public $correct;
23
    public $comment;
24
    public $weighting;
25
    public $position;
26
    public $hotspot_coordinates;
27
    public $hotspot_type;
28
    public $destination;
29
    // these arrays are used to save temporarily new answers
30
    // then they are moved into the arrays above or deleted in the event of cancellation
31
    public $new_answer;
32
    public $new_correct;
33
    public $new_comment;
34
    public $new_weighting;
35
    public $new_position;
36
    public $new_hotspot_coordinates;
37
    public $new_hotspot_type;
38
    public $autoId;
39
    /** @var int Number of answers in the question */
40
    public $nbrAnswers;
41
    public $new_nbrAnswers;
42
    public $new_destination; // id of the next question if feedback option is set to Directfeedback
43
    public $course; //Course information
44
    public $iid;
45
    public $questionJSId;
46
    public $standalone;
47
    /** @var Exercise|null */
48
    private $exercise;
49
50
    /**
51
     * @author Olivier Brouckaert
52
     *
53
     * @param int      $questionId that answers belong to
54
     * @param int      $course_id
55
     * @param Exercise $exercise
56
     * @param bool     $readAnswer
57
     */
58
    public function __construct($questionId, $course_id = 0, $exercise = null, $readAnswer = true)
59
    {
60
        $this->questionId = (int) $questionId;
61
        $this->answer = [];
62
        $this->correct = [];
63
        $this->comment = [];
64
        $this->weighting = [];
65
        $this->position = [];
66
        $this->hotspot_coordinates = [];
67
        $this->hotspot_type = [];
68
        $this->destination = [];
69
        // clears $new_* arrays
70
        $this->cancel();
71
72
        if (empty($course_id)) {
73
            $course_id = api_get_course_int_id();
74
        }
75
76
        $this->course = api_get_course_info_by_id($course_id);
77
        if (empty($exercise)) {
78
            // fills arrays
79
            $objExercise = new Exercise($course_id);
80
            $exerciseId = $_REQUEST['exerciseId'] ?? null;
81
            $objExercise->read($exerciseId, false);
82
        } else {
83
            $objExercise = $exercise;
84
        }
85
        $this->exercise = $objExercise;
86
87
        if ($readAnswer) {
88
            if ('1' == $objExercise->random_answers && CALCULATED_ANSWER != $this->getQuestionType()) {
89
                $this->readOrderedBy('rand()', ''); // randomize answers
90
            } else {
91
                $this->read(); // natural order
92
            }
93
        }
94
    }
95
96
    /**
97
     * Clears $new_* arrays.
98
     *
99
     * @author Olivier Brouckaert
100
     */
101
    public function cancel()
102
    {
103
        $this->new_answer = [];
104
        $this->new_correct = [];
105
        $this->new_comment = [];
106
        $this->new_weighting = [];
107
        $this->new_position = [];
108
        $this->new_hotspot_coordinates = [];
109
        $this->new_hotspot_type = [];
110
        $this->new_nbrAnswers = 0;
111
        $this->new_destination = [];
112
    }
113
114
    /**
115
     * Reads answer information from the database.
116
     *
117
     * @author Olivier Brouckaert
118
     */
119
    public function read()
120
    {
121
        $table = Database::get_course_table(TABLE_QUIZ_ANSWER);
122
        $questionId = $this->questionId;
123
124
        $sql = "SELECT * FROM $table
125
                WHERE
126
                    question_id ='".$questionId."'
127
                ORDER BY position";
128
129
        $result = Database::query($sql);
130
        $i = 1;
131
132
        // while a record is found
133
        while ($object = Database::fetch_object($result)) {
134
            $this->id[$i] = $object->iid;
135
            $this->autoId[$i] = $object->iid;
136
            $this->iid[$i] = $object->iid;
137
            $this->answer[$i] = $object->answer;
138
            $this->correct[$i] = $object->correct;
139
            $this->comment[$i] = $object->comment;
140
            $this->weighting[$i] = $object->ponderation;
141
            $this->position[$i] = $object->position;
142
            $this->hotspot_coordinates[$i] = $object->hotspot_coordinates;
143
            $this->hotspot_type[$i] = $object->hotspot_type;
144
            $i++;
145
        }
146
        $this->nbrAnswers = $i - 1;
147
    }
148
149
    /**
150
     * @param int $id
151
     *
152
     * @return array
153
     */
154
    public function getAnswerByAutoId($id)
155
    {
156
        foreach ($this->autoId as $key => $autoId) {
157
            if ($autoId == $id) {
158
                return [
159
                    'answer' => $this->answer[$key],
160
                    'correct' => $this->correct[$key],
161
                    'comment' => $this->comment[$key],
162
                ];
163
            }
164
        }
165
166
        return [];
167
    }
168
169
    /**
170
     * returns all answer ids from this question Id.
171
     *
172
     * @author Yoselyn Castillo
173
     *
174
     * @return array - $id (answer ids)
175
     */
176
    public function selectAnswerId()
177
    {
178
        $table = Database::get_course_table(TABLE_QUIZ_ANSWER);
179
        $questionId = $this->questionId;
180
181
        $sql = "SELECT id FROM
182
              $table
183
              WHERE c_id = {$this->course_id} AND question_id ='".$questionId."'";
0 ignored issues
show
Bug introduced by
The property course_id does not exist on Answer. Did you mean course?
Loading history...
184
185
        $result = Database::query($sql);
186
        $id = [];
187
        // while a record is found
188
        if (Database::num_rows($result) > 0) {
189
            while ($object = Database::fetch_array($result)) {
190
                $id[] = $object['id'];
191
            }
192
        }
193
194
        return $id;
195
    }
196
197
    /**
198
     * Reads answer information from the data base ordered by parameter.
199
     *
200
     * @param string $field Field we want to order by
201
     * @param string $order DESC or ASC
202
     *
203
     * @return bool
204
     *
205
     * @author Frederic Vauthier
206
     */
207
    public function readOrderedBy($field, $order = 'ASC')
208
    {
209
        $field = Database::escape_string($field);
210
        if (empty($field)) {
211
            $field = 'position';
212
        }
213
214
        if ('ASC' != $order && 'DESC' != $order) {
215
            $order = 'ASC';
216
        }
217
218
        $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
219
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_QUESTION);
220
        $questionId = (int) $this->questionId;
221
222
        $sql = "SELECT type FROM $TBL_QUIZ
223
                WHERE iid = $questionId";
224
        $result_question = Database::query($sql);
225
        $questionType = Database::fetch_array($result_question);
226
227
        if (DRAGGABLE == $questionType['type']) {
228
            // Random is done by submit.js.tpl
229
            $this->read();
230
231
            return true;
232
        }
233
234
        $sql = "SELECT
235
                    answer,
236
                    correct,
237
                    comment,
238
                    ponderation,
239
                    position,
240
                    hotspot_coordinates,
241
                    hotspot_type,
242
                    iid
243
                FROM $TBL_ANSWER
244
                WHERE
245
                    question_id='".$questionId."'
246
                ORDER BY $field $order";
247
        $result = Database::query($sql);
248
249
        $i = 1;
250
        // while a record is found
251
        $doubt_data = null;
252
        while ($object = Database::fetch_object($result)) {
253
            if (UNIQUE_ANSWER_NO_OPTION == $questionType['type'] && 666 == $object->position) {
254
                $doubt_data = $object;
255
256
                continue;
257
            }
258
            $this->answer[$i] = $object->answer;
259
            $this->correct[$i] = $object->correct;
260
            $this->comment[$i] = $object->comment;
261
            $this->weighting[$i] = $object->ponderation;
262
            $this->position[$i] = $object->position;
263
            $this->hotspot_coordinates[$i] = $object->hotspot_coordinates;
264
            $this->hotspot_type[$i] = $object->hotspot_type;
265
            $this->autoId[$i] = $object->iid;
266
            $this->iid[$i] = $object->iid;
267
            $i++;
268
        }
269
270
        if (UNIQUE_ANSWER_NO_OPTION == $questionType['type'] && !empty($doubt_data)) {
271
            $this->answer[$i] = $doubt_data->answer;
272
            $this->correct[$i] = $doubt_data->correct;
273
            $this->comment[$i] = $doubt_data->comment;
274
            $this->weighting[$i] = $doubt_data->ponderation;
275
            $this->position[$i] = $doubt_data->position;
276
            $this->hotspot_coordinates[$i] = isset($object->hotspot_coordinates) ? $object->hotspot_coordinates : 0;
277
            $this->hotspot_type[$i] = isset($object->hotspot_type) ? $object->hotspot_type : 0;
278
            $this->destination[$i] = $doubt_data->destination;
279
            $this->autoId[$i] = $doubt_data->iid;
280
            $this->iid[$i] = $doubt_data->iid;
281
            $i++;
282
        }
283
        $this->nbrAnswers = $i - 1;
284
285
        return true;
286
    }
287
288
    /**
289
     * returns the autoincrement id.
290
     *
291
     * @author Juan Carlos Ra�a
292
     *
293
     * @return int - answer num
294
     */
295
    public function selectAutoId($id)
296
    {
297
        return isset($this->iid[$id]) ? (int) $this->iid[$id] : 0;
298
    }
299
300
    /**
301
     * returns the number of answers in this question.
302
     *
303
     * @author Olivier Brouckaert
304
     *
305
     * @return int - number of answers
306
     */
307
    public function selectNbrAnswers()
308
    {
309
        return $this->nbrAnswers;
310
    }
311
312
    /**
313
     * returns the question ID which the answers belong to.
314
     *
315
     * @author Olivier Brouckaert
316
     *
317
     * @return int - the question ID
318
     */
319
    public function selectQuestionId()
320
    {
321
        return $this->questionId;
322
    }
323
324
    /**
325
     * returns the question ID of the destination question.
326
     *
327
     * @author Julio Montoya
328
     *
329
     * @param int $id
330
     *
331
     * @return int - the question ID
332
     */
333
    public function selectDestination($id)
334
    {
335
        return isset($this->destination[$id]) ? $this->destination[$id] : null;
336
    }
337
338
    /**
339
     * returns the answer title.
340
     *
341
     * @author Olivier Brouckaert
342
     *
343
     * @param - integer $id - answer ID
344
     *
345
     * @return string - answer title
346
     */
347
    public function selectAnswer($id)
348
    {
349
        return isset($this->answer[$id]) ? $this->answer[$id] : null;
350
    }
351
352
    /**
353
     * return array answer by id else return a bool.
354
     *
355
     * @param int $auto_id
356
     *
357
     * @return array
358
     */
359
    public function selectAnswerByAutoId($auto_id)
360
    {
361
        $table = Database::get_course_table(TABLE_QUIZ_ANSWER);
362
        $auto_id = (int) $auto_id;
363
        $sql = "SELECT iid, answer FROM $table
364
                WHERE iid='$auto_id'";
365
        $rs = Database::query($sql);
366
367
        if (Database::num_rows($rs) > 0) {
368
            return Database::fetch_assoc($rs);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Database::fetch_assoc($rs) also could return the type boolean which is incompatible with the documented return type array.
Loading history...
369
        }
370
371
        return false;
372
    }
373
374
    /**
375
     * returns the answer title from an answer's position.
376
     *
377
     * @author Yannick Warnier
378
     *
379
     * @param - integer $pos - answer ID
380
     *
381
     * @return bool - answer title
382
     */
383
    public function selectAnswerIdByPosition($pos)
384
    {
385
        foreach ($this->position as $k => $v) {
386
            if ($v != $pos) {
387
                continue;
388
            }
389
390
            return $k;
391
        }
392
393
        return false;
394
    }
395
396
    /**
397
     * Returns a list of answers.
398
     *
399
     * @author Yannick Warnier <[email protected]>
400
     *
401
     * @param bool $decode
402
     *
403
     * @return array List of answers where each answer is an array
404
     *               of (id, answer, comment, grade) and grade=weighting
405
     */
406
    public function getAnswersList($decode = false)
407
    {
408
        $list = [];
409
        for ($i = 1; $i <= $this->nbrAnswers; $i++) {
410
            if (!empty($this->answer[$i])) {
411
                //Avoid problems when parsing elements with accents
412
                if ($decode) {
413
                    $this->answer[$i] = api_html_entity_decode(
414
                        $this->answer[$i],
415
                        ENT_QUOTES,
416
                        api_get_system_encoding()
417
                    );
418
                    $this->comment[$i] = api_html_entity_decode(
419
                        $this->comment[$i],
420
                        ENT_QUOTES,
421
                        api_get_system_encoding()
422
                    );
423
                }
424
425
                $list[] = [
426
                    'id' => $i,
427
                    'answer' => $this->answer[$i],
428
                    'comment' => $this->comment[$i],
429
                    'grade' => $this->weighting[$i],
430
                    'hotspot_coord' => $this->hotspot_coordinates[$i],
431
                    'hotspot_type' => $this->hotspot_type[$i],
432
                    'correct' => $this->correct[$i],
433
                ];
434
            }
435
        }
436
437
        return $list;
438
    }
439
440
    /**
441
     * Returns a list of grades.
442
     *
443
     * @author Yannick Warnier <[email protected]>
444
     *
445
     * @return array List of grades where grade=weighting (?)
446
     */
447
    public function getGradesList()
448
    {
449
        $list = [];
450
        for ($i = 0; $i < $this->nbrAnswers; $i++) {
451
            if (!empty($this->answer[$i])) {
452
                $list[$i] = $this->weighting[$i];
453
            }
454
        }
455
456
        return $list;
457
    }
458
459
    /**
460
     * Returns the question type.
461
     *
462
     * @author    Yannick Warnier <[email protected]>
463
     *
464
     * @todo remove this function use CQuizQuestion
465
     *
466
     * @return int The type of the question this answer is bound to
467
     */
468
    public function getQuestionType()
469
    {
470
        $table = Database::get_course_table(TABLE_QUIZ_QUESTION);
471
        $sql = "SELECT type FROM $table
472
                WHERE iid = '".$this->questionId."'";
473
        $res = Database::query($sql);
474
        if (Database::num_rows($res) <= 0) {
475
            return null;
476
        }
477
        $row = Database::fetch_array($res);
478
479
        return (int) $row['type'];
480
    }
481
482
    /**
483
     * tells if answer is correct or not.
484
     *
485
     * @author Olivier Brouckaert
486
     *
487
     * @param - integer $id - answer ID
488
     *
489
     * @return int - 0 if bad answer, not 0 if good answer
490
     */
491
    public function isCorrect($id)
492
    {
493
        return isset($this->correct[$id]) ? $this->correct[$id] : null;
494
    }
495
496
    /**
497
     * returns answer comment.
498
     *
499
     * @author Olivier Brouckaert
500
     *
501
     * @param - integer $id - answer ID
502
     *
503
     * @return string - answer comment
504
     */
505
    public function selectComment($id)
506
    {
507
        return isset($this->comment[$id]) ? $this->comment[$id] : null;
508
    }
509
510
    /**
511
     * returns answer weighting.
512
     *
513
     * @author Olivier Brouckaert
514
     *
515
     * @param - integer $id - answer ID
516
     *
517
     * @return int - answer weighting
518
     */
519
    public function selectWeighting($id)
520
    {
521
        return isset($this->weighting[$id]) ? $this->weighting[$id] : null;
522
    }
523
524
    /**
525
     * returns answer position.
526
     *
527
     * @author Olivier Brouckaert
528
     *
529
     * @param - integer $id - answer ID
530
     *
531
     * @return int - answer position
532
     */
533
    public function selectPosition($id)
534
    {
535
        return isset($this->position[$id]) ? $this->position[$id] : null;
536
    }
537
538
    /**
539
     * returns answer hotspot coordinates.
540
     *
541
     * @author Olivier Brouckaert
542
     *
543
     * @param int $id Answer ID
544
     *
545
     * @return int Answer position
546
     */
547
    public function selectHotspotCoordinates($id)
548
    {
549
        return isset($this->hotspot_coordinates[$id]) ? $this->hotspot_coordinates[$id] : null;
550
    }
551
552
    /**
553
     * returns answer hotspot type.
554
     *
555
     * @author Toon Keppens
556
     *
557
     * @param int $id Answer ID
558
     *
559
     * @return int Answer position
560
     */
561
    public function selectHotspotType($id)
562
    {
563
        return isset($this->hotspot_type[$id]) ? $this->hotspot_type[$id] : null;
564
    }
565
566
    /**
567
     * Creates a new answer.
568
     *
569
     * @author Olivier Brouckaert
570
     *
571
     * @param string $answer                  answer title
572
     * @param int    $correct                 0 if bad answer, not 0 if good answer
573
     * @param string $comment                 answer comment
574
     * @param int    $weighting               answer weighting
575
     * @param int    $position                answer position
576
     * @param array  $new_hotspot_coordinates Coordinates for hotspot exercises (optional)
577
     * @param int    $new_hotspot_type        Type for hotspot exercises (optional)
578
     * @param string $destination
579
     */
580
    public function createAnswer(
581
        $answer,
582
        $correct,
583
        $comment,
584
        $weighting,
585
        $position,
586
        $new_hotspot_coordinates = null,
587
        $new_hotspot_type = null,
588
        $destination = ''
589
    ) {
590
        $this->new_nbrAnswers++;
591
        $id = $this->new_nbrAnswers;
592
        $this->new_answer[$id] = $answer;
593
        $this->new_correct[$id] = $correct;
594
        $this->new_comment[$id] = $comment;
595
        $this->new_weighting[$id] = $weighting;
596
        $this->new_position[$id] = $position;
597
        $this->new_hotspot_coordinates[$id] = $new_hotspot_coordinates;
598
        $this->new_hotspot_type[$id] = $new_hotspot_type;
599
    }
600
601
    /**
602
     * Updates an answer.
603
     *
604
     * @author Toon Keppens
605
     *
606
     * @param int    $iid
607
     * @param string $answer
608
     * @param string $comment
609
     * @param string $correct
610
     * @param string $weighting
611
     * @param string $position
612
     * @param string $destination
613
     * @param string $hotSpotCoordinates
614
     * @param string $hotSpotType
615
     *
616
     * @return CQuizAnswer
617
     */
618
    public function updateAnswers(
619
        $iid,
620
        $answer,
621
        $comment,
622
        $correct,
623
        $weighting,
624
        $position,
625
        $destination,
626
        $hotSpotCoordinates,
627
        $hotSpotType
628
    ) {
629
        $em = Database::getManager();
630
631
        /** @var CQuizAnswer $quizAnswer */
632
        $quizAnswer = $em->find(CQuizAnswer::class, $iid);
633
        if ($quizAnswer) {
0 ignored issues
show
introduced by
$quizAnswer is of type Chamilo\CourseBundle\Entity\CQuizAnswer, thus it always evaluated to true.
Loading history...
634
            $quizAnswer
635
                ->setAnswer((string) $answer)
636
                ->setComment((string) $comment)
637
                ->setCorrect((int) $correct)
638
                ->setPonderation($weighting)
639
                ->setPosition((int) $position)
640
                ->setHotspotCoordinates($hotSpotCoordinates)
641
                ->setHotspotType($hotSpotType)
642
            ;
643
644
            $em->persist($quizAnswer);
645
            $em->flush();
646
647
            return $quizAnswer;
648
        }
649
650
        return false;
651
    }
652
653
    /**
654
     * Records answers into the data base.
655
     *
656
     * @author Olivier Brouckaert
657
     */
658
    public function save()
659
    {
660
        $answerTable = Database::get_course_table(TABLE_QUIZ_ANSWER);
661
        $em = Database::getManager();
662
        $questionId = (int) $this->questionId;
663
664
        $courseId = $this->course['real_id'];
665
        $answerList = [];
666
        $questionRepo = Container::getQuestionRepository();
667
        /** @var CQuizQuestion $question */
668
        $question = $questionRepo->find($questionId);
669
        $questionType = $this->getQuestionType();
670
671
        for ($i = 1; $i <= $this->new_nbrAnswers; $i++) {
672
            $answer = (string) ($this->new_answer[$i] ?? '');
673
            $correct = isset($this->new_correct[$i]) ? (int) $this->new_correct[$i] : null;
674
            $comment = $this->new_comment[$i] ?? null;
675
            $weighting = isset($this->new_weighting[$i]) ? (float) $this->new_weighting[$i] : null;
676
            $position = isset($this->new_position[$i]) ? (int) $this->new_position[$i] : (int) $i;
677
            $hotspot_coordinates = $this->new_hotspot_coordinates[$i] ?? null;
678
            $hotspot_type = $this->new_hotspot_type[$i] ?? null;
679
            $iid = isset($this->iid[$i]) ? (int) $this->iid[$i] : 0;
680
681
            if (!isset($this->position[$i])) {
682
                $quizAnswer = new CQuizAnswer();
683
                $quizAnswer
684
                    ->setQuestion($question)
685
                    ->setAnswer($answer)
686
                    ->setCorrect($correct)
687
                    ->setComment($comment)
688
                    ->setPonderation(!is_null($weighting) ? $weighting : 0.0)
689
                    ->setPosition($position)
690
                    ->setHotspotCoordinates($hotspot_coordinates)
691
                    ->setHotspotType($hotspot_type);
692
693
                $em->persist($quizAnswer);
694
                $em->flush();
695
696
                $iid = (int) $quizAnswer->getIid();
697
698
                if ($iid) {
699
                    if (in_array(
700
                        $questionType,
701
                        [MATCHING, MATCHING_COMBINATION, MATCHING_DRAGGABLE, MATCHING_DRAGGABLE_COMBINATION],
702
                        true
703
                    )) {
704
                        $answerObj = new self($this->questionId, $courseId, $this->exercise, false);
705
                        $answerObj->read();
706
                        $correctAnswerId = $answerObj->selectAnswerIdByPosition($correct);
707
708
                        if (in_array($questionType, [MATCHING, MATCHING_COMBINATION], true) && !$correctAnswerId) {
709
                            continue;
710
                        }
711
                        $correctAnswerAutoId = $answerObj->selectAutoId($correct);
712
                        $quizAnswer->setCorrect($correctAnswerAutoId ?: 0);
713
                    }
714
715
                    $em->persist($quizAnswer);
716
                    $em->flush();
717
                }
718
            } else {
719
                // Update existing answer (note: updateAnswers will cast position to int now)
720
                $this->updateAnswers(
721
                    $iid,
722
                    $this->new_answer[$i],
723
                    $this->new_comment[$i],
724
                    $this->new_correct[$i],
725
                    $this->new_weighting[$i],
726
                    $this->new_position[$i],
727
                    null,
728
                    $this->new_hotspot_coordinates[$i],
729
                    $this->new_hotspot_type[$i]
730
                );
731
            }
732
733
            $answerList[$i] = $iid;
734
        }
735
736
        // Post-processing for draggable/matching types
737
        switch ($questionType) {
738
            case MATCHING_DRAGGABLE:
739
                foreach ($this->new_correct as $value => $status) {
740
                    if (!empty($status)) {
741
                        $correct = $answerList[$status] ?? $status;
742
                        $myAutoId = $answerList[$value];
743
                        $sql = "UPDATE $answerTable SET correct = '$correct' WHERE iid = $myAutoId";
744
                        Database::query($sql);
745
                    }
746
                }
747
                break;
748
749
            case MATCHING_DRAGGABLE_COMBINATION:
750
                foreach ($this->new_correct as $value => $status) {
751
                    if (!empty($status)) {
752
                        $correct = $answerList[$status] ?? $status;
753
                        $myAutoId = $answerList[$value];
754
                        $sql = "UPDATE $answerTable SET correct = '$correct' WHERE iid = $myAutoId";
755
                        Database::query($sql);
756
                    }
757
                }
758
                break;
759
760
            case DRAGGABLE:
761
                foreach ($this->new_correct as $value => $status) {
762
                    if (!empty($status)) {
763
                        $correct = $answerList[$status];
764
                        $myAutoId = $answerList[$value];
765
                        $sql = "UPDATE $answerTable SET correct = '$correct' WHERE iid = $myAutoId";
766
                        Database::query($sql);
767
                    }
768
                }
769
                break;
770
        }
771
772
        // Delete trailing answers when we saved fewer than previously existed
773
        if (count($this->position) > $this->new_nbrAnswers) {
774
            $i = $this->new_nbrAnswers + 1;
775
            while (isset($this->position[$i])) {
776
                $toDeletePos = (int) $this->position[$i];
777
                $sql = "DELETE FROM $answerTable
778
                    WHERE question_id = '".$questionId."' AND position = '$toDeletePos'";
779
                Database::query($sql);
780
                $i++;
781
            }
782
        }
783
784
        // Move new_* arrays into main arrays
785
        $this->answer               = $this->new_answer;
786
        $this->correct              = $this->new_correct;
787
        $this->comment              = $this->new_comment;
788
        $this->weighting            = $this->new_weighting;
789
        $this->position             = $this->new_position;
790
        $this->hotspot_coordinates  = $this->new_hotspot_coordinates;
791
        $this->hotspot_type         = $this->new_hotspot_type;
792
        $this->nbrAnswers           = $this->new_nbrAnswers;
793
794
        $this->cancel();
795
    }
796
797
    /**
798
     * Duplicates answers by copying them into another question.
799
     *
800
     * @author Olivier Brouckaert
801
     *
802
     * @param null $courseInfo destination course info (result of the function api_get_course_info() )
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $courseInfo is correct as it would always require null to be passed?
Loading history...
803
     *
804
     * @throws \Doctrine\ORM\ORMException
805
     * @throws \Doctrine\ORM\OptimisticLockException
806
     */
807
    public function duplicate(Question $newQuestion, $courseInfo = null)
808
    {
809
        $newQuestionId = $newQuestion->id;
810
811
        $em = Database::getManager();
812
        $questionRepo = Container::getQuestionRepository();
813
        $answerRepo = $em->getRepository(CQuizAnswer::class);
814
        $question = $questionRepo->find($newQuestionId);
815
816
        if (empty($courseInfo)) {
817
            $courseInfo = $this->course;
818
        }
819
820
        $fixedList = [];
821
822
        if (MULTIPLE_ANSWER_TRUE_FALSE === self::getQuestionType() ||
0 ignored issues
show
Bug Best Practice introduced by
The method Answer::getQuestionType() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

822
        if (MULTIPLE_ANSWER_TRUE_FALSE === self::/** @scrutinizer ignore-call */ getQuestionType() ||
Loading history...
823
            MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY === self::getQuestionType()
824
        ) {
825
            // Selecting origin options
826
            $originOptions = Question::readQuestionOption($this->selectQuestionId());
827
828
            if (!empty($originOptions)) {
829
                foreach ($originOptions as $item) {
830
                    $newOptionList[] = $item['iid'];
831
                }
832
            }
833
834
            $destinationOptions = Question::readQuestionOption($newQuestionId);
835
            $i = 0;
836
            if (!empty($destinationOptions)) {
837
                foreach ($destinationOptions as $item) {
838
                    $fixedList[$newOptionList[$i]] = $item['iid'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $newOptionList does not seem to be defined for all execution paths leading up to this point.
Loading history...
839
                    $i++;
840
                }
841
            }
842
        }
843
844
        // if at least one answer
845
        if (empty($this->nbrAnswers)) {
846
            return;
847
        }
848
        // inserts new answers into data base
849
        $correctAnswers = [];
850
        $onlyAnswers = [];
851
        $allAnswers = [];
852
853
        if (in_array($newQuestion->type, [MATCHING, MATCHING_COMBINATION, MATCHING_DRAGGABLE, MATCHING_DRAGGABLE_COMBINATION], true)) {
854
            $temp = [];
855
            for ($i = 1; $i <= $this->nbrAnswers; $i++) {
856
                $answer = [
857
                    'id' => $this->id[$i],
858
                    'answer' => $this->answer[$i],
859
                    'correct' => $this->correct[$i],
860
                    'comment' => $this->comment[$i],
861
                    'weighting' => $this->weighting[$i],
862
                    'ponderation' => $this->weighting[$i],
863
                    'position' => $this->position[$i],
864
                    'hotspot_coordinates' => $this->hotspot_coordinates[$i],
865
                    'hotspot_type' => $this->hotspot_type[$i],
866
                ];
867
                $temp[$answer['position']] = $answer;
868
                $allAnswers[$this->id[$i]] = $this->answer[$i];
869
            }
870
871
            foreach ($temp as $answer) {
872
                if (!empty($this->course['id']) && ($this->course['id'] != $courseInfo['id'])) {
873
                    // check resources inside html from ckeditor tool and copy correct urls into recipient course
874
                    $answer['answer'] = DocumentManager::replaceUrlWithNewCourseCode(
875
                        $answer['answer'],
876
                        $this->course['id'],
877
                        $courseInfo['id']
878
                    );
879
880
                    $answer['comment'] = DocumentManager::replaceUrlWithNewCourseCode(
881
                        $answer['comment'],
882
                        $this->course['id'],
883
                        $courseInfo['id']
884
                    );
885
                }
886
887
                $quizAnswer = new CQuizAnswer();
888
                $quizAnswer
889
                    ->setQuestion($question)
890
                    ->setAnswer($answer['answer'])
891
                    ->setCorrect($answer['correct'])
892
                    ->setComment($answer['comment'])
893
                    ->setPonderation($answer['ponderation'])
894
                    ->setPosition($answer['position'])
895
                    ->setHotspotCoordinates($answer['hotspot_coordinates'])
896
                    ->setHotspotType($answer['hotspot_type'])
897
                ;
898
899
                $em->persist($quizAnswer);
900
                $em->flush();
901
902
                $answerId = $quizAnswer->getIid();
903
904
                if ($answerId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $answerId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
905
                    $em->persist($quizAnswer);
906
                    $em->flush();
907
908
                    $correctAnswers[$answerId] = $answer['correct'];
909
                    $onlyAnswers[$answerId] = $answer['answer'];
910
                }
911
            }
912
        } else {
913
            for ($i = 1; $i <= $this->nbrAnswers; $i++) {
914
                if (!empty($this->course['id']) && ($this->course['id'] != $courseInfo['id'])) {
915
                    $this->answer[$i] = DocumentManager::replaceUrlWithNewCourseCode(
916
                        $this->answer[$i],
917
                        $this->course['id'],
918
                        $courseInfo['id']
919
                    );
920
                    $this->comment[$i] = DocumentManager::replaceUrlWithNewCourseCode(
921
                        $this->comment[$i],
922
                        $this->course['id'],
923
                        $courseInfo['id']
924
                    );
925
                }
926
927
                $correct = $this->correct[$i];
928
                if (MULTIPLE_ANSWER_TRUE_FALSE == $newQuestion->type ||
929
                    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY == $newQuestion->type
930
                ) {
931
                    $correct = $fixedList[(int) $correct];
932
                }
933
934
                $quizAnswer = new CQuizAnswer();
935
                $quizAnswer
936
                    ->setQuestion($question)
937
                    ->setAnswer($this->answer[$i])
938
                    ->setCorrect($correct)
939
                    ->setComment($this->comment[$i])
940
                    ->setPonderation($this->weighting[$i])
941
                    ->setPosition($this->position[$i])
942
                    ->setHotspotCoordinates($this->hotspot_coordinates[$i])
943
                    ->setHotspotType($this->hotspot_type[$i]);
944
945
                $em->persist($quizAnswer);
946
                $em->flush();
947
948
                $answerId = $quizAnswer->getIid();
949
950
                $correctAnswers[$answerId] = $correct;
951
                $onlyAnswers[$answerId] = $this->answer[$i];
952
                $allAnswers[$this->id[$i]] = $this->answer[$i];
953
            }
954
        }
955
956
        // Fix correct answers
957
        if (in_array($newQuestion->type, [DRAGGABLE, MATCHING, MATCHING_COMBINATION, MATCHING_DRAGGABLE, MATCHING_DRAGGABLE_COMBINATION], true)) {
958
            $onlyAnswersFlip = array_flip($onlyAnswers);
959
            foreach ($correctAnswers as $answerIdItem => $correctAnswer) {
960
                if (!isset($allAnswers[$correctAnswer]) || !isset($onlyAnswersFlip[$allAnswers[$correctAnswer]])) {
961
                    continue;
962
                }
963
                $answer = $answerRepo->find($answerIdItem);
964
                $answer->setCorrect((int) $onlyAnswersFlip[$allAnswers[$correctAnswer]]);
965
966
                $em->persist($answer);
967
                $em->flush();
968
            }
969
        }
970
    }
971
972
    /**
973
     * Get the necessary JavaScript for some answers.
974
     *
975
     * @return string
976
     */
977
    public function getJs()
978
    {
979
        return "<script>
980
                $(window).on('load', function() {
981
                    jsPlumb.ready(function() {
982
                        if ($('#drag{$this->questionId}_question').length > 0) {
983
                            setTimeout(function() {
984
                                // Initialize MatchingDraggable
985
                                MatchingDraggable.init('{$this->questionId}');
986
                            }, 1000);
987
                        }
988
                    });
989
                });
990
            </script>";
991
    }
992
993
    /**
994
     * Check if a answer is correct by an answer auto id.
995
     *
996
     * @param int $needle The answer auto id
997
     *
998
     * @return bool
999
     */
1000
    public function isCorrectByAutoId($needle)
1001
    {
1002
        $key = 0;
1003
        if (is_array($this->autoId)) {
1004
            foreach ($this->autoId as $autoIdKey => $autoId) {
1005
                if ($autoId == $needle) {
1006
                    $key = $autoIdKey;
1007
                }
1008
            }
1009
        }
1010
1011
        if (!$key) {
1012
            return false;
1013
        }
1014
1015
        return $this->isCorrect($key) ? true : false;
1016
    }
1017
1018
    /**
1019
     * Get answers already added to question.
1020
     *
1021
     * @return array
1022
     */
1023
    public function getAnswers()
1024
    {
1025
        $table = Database::get_course_table(TABLE_QUIZ_ANSWER);
1026
        $questionId = $this->questionId;
1027
1028
        $sql = "SELECT * FROM $table
1029
                WHERE question_id = $questionId
1030
                ORDER BY position";
1031
1032
        $result = Database::query($sql);
1033
1034
        $answers = [];
1035
1036
        // while a record is found
1037
        while ($answer = Database::fetch_assoc($result)) {
1038
            $answers[] = $answer;
1039
        }
1040
1041
        return $answers;
1042
    }
1043
1044
}
1045