Answer::createAnswer()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 8
dl 0
loc 19
rs 9.9666
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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