Passed
Pull Request — master (#6637)
by
unknown
14:37 queued 06:29
created

Answer::getAnswers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

840
        if (MULTIPLE_ANSWER_TRUE_FALSE === self::/** @scrutinizer ignore-call */ getQuestionType() ||
Loading history...
841
            MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY === self::getQuestionType()
842
        ) {
843
            // Selecting origin options
844
            $originOptions = Question::readQuestionOption($this->selectQuestionId());
845
846
            if (!empty($originOptions)) {
847
                foreach ($originOptions as $item) {
848
                    $newOptionList[] = $item['iid'];
849
                }
850
            }
851
852
            $destinationOptions = Question::readQuestionOption($newQuestionId);
853
            $i = 0;
854
            if (!empty($destinationOptions)) {
855
                foreach ($destinationOptions as $item) {
856
                    $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...
857
                    $i++;
858
                }
859
            }
860
        }
861
862
        // if at least one answer
863
        if (empty($this->nbrAnswers)) {
864
            return;
865
        }
866
        // inserts new answers into data base
867
        $correctAnswers = [];
868
        $onlyAnswers = [];
869
        $allAnswers = [];
870
871
        if (in_array($newQuestion->type, [MATCHING, MATCHING_COMBINATION, MATCHING_DRAGGABLE, MATCHING_DRAGGABLE_COMBINATION], true)) {
872
            $temp = [];
873
            for ($i = 1; $i <= $this->nbrAnswers; $i++) {
874
                $answer = [
875
                    'id' => $this->id[$i],
876
                    'answer' => $this->answer[$i],
877
                    'correct' => $this->correct[$i],
878
                    'comment' => $this->comment[$i],
879
                    'weighting' => $this->weighting[$i],
880
                    'ponderation' => $this->weighting[$i],
881
                    'position' => $this->position[$i],
882
                    'hotspot_coordinates' => $this->hotspot_coordinates[$i],
883
                    'hotspot_type' => $this->hotspot_type[$i],
884
                ];
885
                $temp[$answer['position']] = $answer;
886
                $allAnswers[$this->id[$i]] = $this->answer[$i];
887
            }
888
889
            foreach ($temp as $answer) {
890
                if ($this->course['id'] != $courseInfo['id']) {
891
                    // check resources inside html from ckeditor tool and copy correct urls into recipient course
892
                    $answer['answer'] = DocumentManager::replaceUrlWithNewCourseCode(
893
                        $answer['answer'],
894
                        $this->course['id'],
895
                        $courseInfo['id']
896
                    );
897
898
                    $answer['comment'] = DocumentManager::replaceUrlWithNewCourseCode(
899
                        $answer['comment'],
900
                        $this->course['id'],
901
                        $courseInfo['id']
902
                    );
903
                }
904
905
                $quizAnswer = new CQuizAnswer();
906
                $quizAnswer
907
                    ->setQuestion($question)
908
                    ->setAnswer($answer['answer'])
909
                    ->setCorrect($answer['correct'])
910
                    ->setComment($answer['comment'])
911
                    ->setPonderation($answer['ponderation'])
912
                    ->setPosition($answer['position'])
913
                    ->setHotspotCoordinates($answer['hotspot_coordinates'])
914
                    ->setHotspotType($answer['hotspot_type'])
915
                ;
916
917
                $em->persist($quizAnswer);
918
                $em->flush();
919
920
                $answerId = $quizAnswer->getIid();
921
922
                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...
923
                    $em->persist($quizAnswer);
924
                    $em->flush();
925
926
                    $correctAnswers[$answerId] = $answer['correct'];
927
                    $onlyAnswers[$answerId] = $answer['answer'];
928
                }
929
            }
930
        } else {
931
            for ($i = 1; $i <= $this->nbrAnswers; $i++) {
932
                if ($this->course['id'] != $courseInfo['id']) {
933
                    $this->answer[$i] = DocumentManager::replaceUrlWithNewCourseCode(
934
                        $this->answer[$i],
935
                        $this->course['id'],
936
                        $courseInfo['id']
937
                    );
938
                    $this->comment[$i] = DocumentManager::replaceUrlWithNewCourseCode(
939
                        $this->comment[$i],
940
                        $this->course['id'],
941
                        $courseInfo['id']
942
                    );
943
                }
944
945
                $correct = $this->correct[$i];
946
                if (MULTIPLE_ANSWER_TRUE_FALSE == $newQuestion->type ||
947
                    MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY == $newQuestion->type
948
                ) {
949
                    $correct = $fixedList[(int) $correct];
950
                }
951
952
                $quizAnswer = new CQuizAnswer();
953
                $quizAnswer
954
                    ->setQuestion($question)
955
                    ->setAnswer($this->answer[$i])
956
                    ->setCorrect($correct)
957
                    ->setComment($this->comment[$i])
958
                    ->setPonderation($this->weighting[$i])
959
                    ->setPosition($this->position[$i])
960
                    ->setHotspotCoordinates($this->hotspot_coordinates[$i])
961
                    ->setHotspotType($this->hotspot_type[$i]);
962
963
                $em->persist($quizAnswer);
964
                $em->flush();
965
966
                $answerId = $quizAnswer->getIid();
967
968
                $correctAnswers[$answerId] = $correct;
969
                $onlyAnswers[$answerId] = $this->answer[$i];
970
                $allAnswers[$this->id[$i]] = $this->answer[$i];
971
            }
972
        }
973
974
        // Fix correct answers
975
        if (in_array($newQuestion->type, [DRAGGABLE, MATCHING, MATCHING_COMBINATION, MATCHING_DRAGGABLE, MATCHING_DRAGGABLE_COMBINATION], true)) {
976
            $onlyAnswersFlip = array_flip($onlyAnswers);
977
            foreach ($correctAnswers as $answerIdItem => $correctAnswer) {
978
                if (!isset($allAnswers[$correctAnswer]) || !isset($onlyAnswersFlip[$allAnswers[$correctAnswer]])) {
979
                    continue;
980
                }
981
                $answer = $answerRepo->find($answerIdItem);
982
                $answer->setCorrect((int) $onlyAnswersFlip[$allAnswers[$correctAnswer]]);
983
984
                $em->persist($answer);
985
                $em->flush();
986
            }
987
        }
988
    }
989
990
    /**
991
     * Get the necessary JavaScript for some answers.
992
     *
993
     * @return string
994
     */
995
    public function getJs()
996
    {
997
        return "<script>
998
                $(window).on('load', function() {
999
                    jsPlumb.ready(function() {
1000
                        if ($('#drag{$this->questionId}_question').length > 0) {
1001
                            setTimeout(function() {
1002
                                // Initialize MatchingDraggable
1003
                                MatchingDraggable.init('{$this->questionId}');
1004
                            }, 1000);
1005
                        }
1006
                    });
1007
                });
1008
            </script>";
1009
    }
1010
1011
    /**
1012
     * Check if a answer is correct by an answer auto id.
1013
     *
1014
     * @param int $needle The answer auto id
1015
     *
1016
     * @return bool
1017
     */
1018
    public function isCorrectByAutoId($needle)
1019
    {
1020
        $key = 0;
1021
        if (is_array($this->autoId)) {
1022
            foreach ($this->autoId as $autoIdKey => $autoId) {
1023
                if ($autoId == $needle) {
1024
                    $key = $autoIdKey;
1025
                }
1026
            }
1027
        }
1028
1029
        if (!$key) {
1030
            return false;
1031
        }
1032
1033
        return $this->isCorrect($key) ? true : false;
1034
    }
1035
1036
    /**
1037
     * Get answers already added to question.
1038
     *
1039
     * @return array
1040
     */
1041
    public function getAnswers()
1042
    {
1043
        $table = Database::get_course_table(TABLE_QUIZ_ANSWER);
1044
        $questionId = $this->questionId;
1045
1046
        $sql = "SELECT * FROM $table
1047
                WHERE question_id = $questionId
1048
                ORDER BY position";
1049
1050
        $result = Database::query($sql);
1051
1052
        $answers = [];
1053
1054
        // while a record is found
1055
        while ($answer = Database::fetch_assoc($result)) {
1056
            $answers[] = $answer;
1057
        }
1058
1059
        return $answers;
1060
    }
1061
1062
}
1063