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

Answer::updateAnswers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 33
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 15
nc 2
nop 9
dl 0
loc 33
rs 9.7666
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
            $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