Completed
Push — master ( 27e209...a08afa )
by Julito
186:04 queued 150:53
created

Answer::save()   F

Complexity

Conditions 26
Paths 13830

Size

Total Lines 158
Code Lines 110

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 26
eloc 110
nc 13830
nop 0
dl 0
loc 158
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CourseBundle\Entity\CQuizAnswer;
5
6
/**
7
 * Class Answer
8
 * Allows to instantiate an object of type Answer
9
 * 5 arrays are created to receive the attributes of each answer belonging to a specified question
10
 * @package chamilo.exercise
11
 *
12
 * @author Olivier Brouckaert
13
 */
14
class Answer
15
{
16
    public $questionId;
17
18
    // these are arrays
19
    public $answer;
20
    public $correct;
21
    public $comment;
22
    public $weighting;
23
    public $position;
24
    public $hotspot_coordinates;
25
    public $hotspot_type;
26
    public $destination;
27
    // these arrays are used to save temporarily new answers
28
    // then they are moved into the arrays above or deleted in the event of cancellation
29
    public $new_answer;
30
    public $new_correct;
31
    public $new_comment;
32
    public $new_weighting;
33
    public $new_position;
34
    public $new_hotspot_coordinates;
35
    public $new_hotspot_type;
36
    public $autoId;
37
    public $nbrAnswers;
38
    public $new_nbrAnswers;
39
    public $new_destination; // id of the next question if feedback option is set to Directfeedback
40
    public $course; //Course information
41
    public $iid;
42
    public $questionJSId;
43
    public $standalone;
44
45
    /**
46
     * constructor of the class
47
     *
48
     * @author Olivier Brouckaert
49
     * @param int $questionId that answers belong to
50
     * @param int $course_id
51
     * @param Exercise $exercise
52
     */
53
    public function __construct($questionId, $course_id = 0, $exercise = null)
54
    {
55
        $this->questionId = intval($questionId);
56
        $this->answer = [];
57
        $this->correct = [];
58
        $this->comment = [];
59
        $this->weighting = [];
60
        $this->position = [];
61
        $this->hotspot_coordinates = [];
62
        $this->hotspot_type = [];
63
        $this->destination = [];
64
        // clears $new_* arrays
65
        $this->cancel();
66
67
        if (!empty($course_id)) {
68
            $courseInfo = api_get_course_info_by_id($course_id);
69
        } else {
70
            $courseInfo = api_get_course_info();
71
        }
72
73
        $this->course = $courseInfo;
74
        $this->course_id = $courseInfo['real_id'];
0 ignored issues
show
Bug Best Practice introduced by
The property course_id does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
75
76
        if (empty($exercise)) {
77
            // fills arrays
78
            $objExercise = new Exercise($this->course_id);
79
            $exerciseId = isset($_REQUEST['exerciseId']) ? $_REQUEST['exerciseId'] : null;
80
            $objExercise->read($exerciseId);
81
        } else {
82
            $objExercise = $exercise;
83
        }
84
85
        if ($objExercise->random_answers == '1' && $this->getQuestionType() != CALCULATED_ANSWER) {
86
            $this->readOrderedBy('rand()', ''); // randomize answers
87
        } else {
88
            $this->read(); // natural order
89
        }
90
    }
91
92
    /**
93
     * Clears $new_* arrays
94
     *
95
     * @author Olivier Brouckaert
96
     */
97
    public function cancel()
98
    {
99
        $this->new_answer = [];
100
        $this->new_correct = [];
101
        $this->new_comment = [];
102
        $this->new_weighting = [];
103
        $this->new_position = [];
104
        $this->new_hotspot_coordinates = [];
105
        $this->new_hotspot_type = [];
106
        $this->new_nbrAnswers = 0;
107
        $this->new_destination = [];
108
    }
109
110
    /**
111
     * Reads answer information from the database
112
     *
113
     * @author Olivier Brouckaert
114
     */
115
    public function read()
116
    {
117
        $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
118
        $questionId = $this->questionId;
119
120
        $sql = "SELECT * FROM $TBL_ANSWER
121
                WHERE
122
                    c_id = {$this->course_id} AND
123
                    question_id ='".$questionId."'
124
                ORDER BY position";
125
126
        $result = Database::query($sql);
127
        $i = 1;
128
129
        // while a record is found
130
        while ($object = Database::fetch_object($result)) {
131
            $this->id[$i] = $object->id;
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
132
            $this->answer[$i] = $object->answer;
133
            $this->correct[$i] = $object->correct;
134
            $this->comment[$i] = $object->comment;
135
            $this->weighting[$i] = $object->ponderation;
136
            $this->position[$i] = $object->position;
137
            $this->hotspot_coordinates[$i] = $object->hotspot_coordinates;
138
            $this->hotspot_type[$i] = $object->hotspot_type;
139
            $this->destination[$i] = $object->destination;
140
            $this->autoId[$i] = $object->id_auto;
141
            $this->iid[$i] = $object->iid;
142
            $i++;
143
        }
144
        $this->nbrAnswers = $i - 1;
145
    }
146
147
    /**
148
     * @param int $id
149
     *
150
     * @return array
151
     */
152
    public function getAnswerByAutoId($id)
153
    {
154
        foreach ($this->autoId as $key => $autoId) {
155
            if ($autoId == $id) {
156
                $result = [
157
                    'answer' => $this->answer[$key],
158
                    'correct' => $this->correct[$key],
159
                    'comment' => $this->comment[$key],
160
                ];
161
162
                return $result;
163
            }
164
        }
165
166
        return [];
167
    }
168
169
    /**
170
    * returns all answer ids from this question Id
171
    *
172
    * @author Yoselyn Castillo
173
    * @return array - $id (answer ids)
174
    */
175
    public function selectAnswerId()
176
    {
177
        $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
178
        $questionId = $this->questionId;
179
180
        $sql = "SELECT id FROM
181
              $TBL_ANSWER
182
              WHERE c_id = {$this->course_id} AND question_id ='".$questionId."'";
183
184
        $result = Database::query($sql);
185
        $id = [];
186
        // while a record is found
187
        if (Database::num_rows($result) > 0) {
188
            while ($object = Database::fetch_array($result)) {
189
                $id[] = $object['id'];
190
            }
191
        }
192
193
        return $id;
194
    }
195
196
    /**
197
     * Reads answer information from the data base ordered by parameter
198
     * @param string $field Field we want to order by
199
     * @param string $order DESC or ASC
200
     * @return bool
201
     *
202
     * @author Frederic Vauthier
203
     */
204
    public function readOrderedBy($field, $order = 'ASC')
205
    {
206
        $field = Database::escape_string($field);
207
        if (empty($field)) {
208
            $field = 'position';
209
        }
210
211
        if ($order != 'ASC' && $order != 'DESC') {
212
            $order = 'ASC';
213
        }
214
215
        $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
216
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_QUESTION);
217
        $questionId = intval($this->questionId);
218
219
        $sql = "SELECT type FROM $TBL_QUIZ
220
                WHERE c_id = {$this->course_id} AND id = $questionId";
221
        $result_question = Database::query($sql);
222
        $questionType = Database::fetch_array($result_question);
223
224
        if ($questionType['type'] == DRAGGABLE) {
225
            // Random is done by submit.js.tpl
226
            $this->read();
227
228
            return true;
229
        }
230
231
        $sql = "SELECT
232
                    answer,
233
                    correct,
234
                    comment,
235
                    ponderation,
236
                    position,
237
                    hotspot_coordinates,
238
                    hotspot_type,
239
                    destination,
240
                    id_auto,
241
                    iid
242
                FROM $TBL_ANSWER
243
                WHERE
244
                    c_id = {$this->course_id} AND
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 ($questionType['type'] == UNIQUE_ANSWER_NO_OPTION && $object->position == 666) {
254
                $doubt_data = $object;
255
                continue;
256
            }
257
            $this->answer[$i] = $object->answer;
258
            $this->correct[$i] = $object->correct;
259
            $this->comment[$i] = $object->comment;
260
            $this->weighting[$i] = $object->ponderation;
261
            $this->position[$i] = $object->position;
262
            $this->hotspot_coordinates[$i] = $object->hotspot_coordinates;
263
            $this->hotspot_type[$i] = $object->hotspot_type;
264
            $this->destination[$i] = $object->destination;
265
            $this->autoId[$i] = $object->id_auto;
266
            $this->iid[$i] = $object->iid;
267
            $i++;
268
        }
269
270
        if ($questionType['type'] == UNIQUE_ANSWER_NO_OPTION && !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->id_auto;
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
     * @return integer - answer num
293
     */
294
    public function selectAutoId($id)
295
    {
296
        return isset($this->autoId[$id]) ? $this->autoId[$id] : 0;
297
    }
298
299
    /**
300
     * returns the number of answers in this question
301
     *
302
     * @author Olivier Brouckaert
303
     * @return integer - number of answers
304
     */
305
    public function selectNbrAnswers()
306
    {
307
        return $this->nbrAnswers;
308
    }
309
310
    /**
311
     * returns the question ID which the answers belong to
312
     *
313
     * @author Olivier Brouckaert
314
     * @return integer - the question ID
315
     */
316
    public function selectQuestionId()
317
    {
318
        return $this->questionId;
319
    }
320
321
    /**
322
     * returns the question ID of the destination question
323
     *
324
     * @author Julio Montoya
325
     * @param integer $id
326
     * @return integer - the question ID
327
     */
328
    public function selectDestination($id)
329
    {
330
        return isset($this->destination[$id]) ? $this->destination[$id] : null;
331
    }
332
333
    /**
334
     * returns the answer title
335
     *
336
     * @author Olivier Brouckaert
337
     * @param - integer $id - answer ID
338
     * @return string - answer title
339
     */
340
    public function selectAnswer($id)
341
    {
342
        return isset($this->answer[$id]) ? $this->answer[$id] : null;
343
    }
344
345
    /**
346
     * return array answer by id else return a bool
347
     * @param integer $auto_id
348
     * @return array
349
     */
350
    public function selectAnswerByAutoId($auto_id)
351
    {
352
        $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
353
354
        $auto_id = intval($auto_id);
355
        $sql = "SELECT id, answer, id_auto FROM $TBL_ANSWER
356
                WHERE c_id = {$this->course_id} AND id_auto='$auto_id'";
357
        $rs = Database::query($sql);
358
359
        if (Database::num_rows($rs) > 0) {
360
            $row = Database::fetch_array($rs, 'ASSOC');
361
362
            return $row;
363
        }
364
365
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
366
    }
367
368
    /**
369
     * returns the answer title from an answer's position
370
     *
371
     * @author Yannick Warnier
372
     * @param - integer $id - answer ID
373
     * @return bool - answer title
374
     */
375
    public function selectAnswerIdByPosition($pos)
376
    {
377
        foreach ($this->position as $k => $v) {
378
            if ($v != $pos) {
379
                continue;
380
            }
381
382
            return $k;
383
        }
384
385
        return false;
386
    }
387
388
    /**
389
     * Returns a list of answers
390
     * @author Yannick Warnier <[email protected]>
391
     * @param bool $decode
392
     * @return array    List of answers where each answer is an array
393
     * of (id, answer, comment, grade) and grade=weighting
394
     */
395
    public function getAnswersList($decode = false)
396
    {
397
        $list = [];
398
        for ($i = 1; $i <= $this->nbrAnswers; $i++) {
399
            if (!empty($this->answer[$i])) {
400
                //Avoid problems when parsing elements with accents
401
                if ($decode) {
402
                    $this->answer[$i] = api_html_entity_decode(
403
                        $this->answer[$i],
404
                        ENT_QUOTES,
405
                        api_get_system_encoding()
406
                    );
407
                    $this->comment[$i] = api_html_entity_decode(
408
                        $this->comment[$i],
409
                        ENT_QUOTES,
410
                        api_get_system_encoding()
411
                    );
412
                }
413
414
                $list[] = [
415
                    'id' => $i,
416
                    'answer' => $this->answer[$i],
417
                    'comment' => $this->comment[$i],
418
                    'grade' => $this->weighting[$i],
419
                    'hotspot_coord' => $this->hotspot_coordinates[$i],
420
                    'hotspot_type' => $this->hotspot_type[$i],
421
                    'correct' => $this->correct[$i],
422
                    'destination' => $this->destination[$i],
423
                ];
424
            }
425
        }
426
427
        return $list;
428
    }
429
430
    /**
431
     * Returns a list of grades
432
     * @author Yannick Warnier <[email protected]>
433
     * @return array    List of grades where grade=weighting (?)
434
     */
435
    public function getGradesList()
436
    {
437
        $list = [];
438
        for ($i = 0; $i < $this->nbrAnswers; $i++) {
439
            if (!empty($this->answer[$i])) {
440
                $list[$i] = $this->weighting[$i];
441
            }
442
        }
443
444
        return $list;
445
    }
446
447
    /**
448
     * Returns the question type
449
     * @author    Yannick Warnier <[email protected]>
450
     * @return    integer    The type of the question this answer is bound to
451
     */
452
    public function getQuestionType()
453
    {
454
        $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
455
        $sql = "SELECT type FROM $TBL_QUESTIONS
456
                WHERE c_id = {$this->course_id} AND id = '".$this->questionId."'";
457
        $res = Database::query($sql);
458
        if (Database::num_rows($res) <= 0) {
459
            return null;
460
        }
461
        $row = Database::fetch_array($res);
462
463
        return $row['type'];
464
    }
465
466
467
    /**
468
     * tells if answer is correct or not
469
     *
470
     * @author Olivier Brouckaert
471
     * @param - integer $id - answer ID
472
     * @return integer - 0 if bad answer, not 0 if good answer
473
     */
474
    public function isCorrect($id)
475
    {
476
        return isset($this->correct[$id]) ? $this->correct[$id] : null;
477
    }
478
479
    /**
480
     * returns answer comment
481
     *
482
     * @author Olivier Brouckaert
483
     * @param - integer $id - answer ID
484
     * @return string - answer comment
485
     */
486
    public function selectComment($id)
487
    {
488
        return isset($this->comment[$id]) ? $this->comment[$id] : null;
489
    }
490
491
    /**
492
     * returns answer weighting
493
     *
494
     * @author Olivier Brouckaert
495
     * @param - integer $id - answer ID
496
497
     * @return integer - answer weighting
498
     */
499
    public function selectWeighting($id)
500
    {
501
        return isset($this->weighting[$id]) ? $this->weighting[$id] : null;
502
    }
503
504
    /**
505
     * returns answer position
506
     *
507
     * @author Olivier Brouckaert
508
     * @param - integer $id - answer ID
509
     * @return integer - answer position
510
     */
511
    public function selectPosition($id)
512
    {
513
        return isset($this->position[$id]) ? $this->position[$id] : null;
514
    }
515
516
    /**
517
     * returns answer hotspot coordinates
518
     *
519
     * @author Olivier Brouckaert
520
     * @param integer $id Answer ID
521
     * @return integer    Answer position
522
     */
523
    public function selectHotspotCoordinates($id)
524
    {
525
        return isset($this->hotspot_coordinates[$id]) ? $this->hotspot_coordinates[$id] : null;
526
    }
527
528
    /**
529
     * returns answer hotspot type
530
     *
531
     * @author Toon Keppens
532
     * @param integer $id Answer ID
533
     * @return integer        Answer position
534
     */
535
    public function selectHotspotType($id)
536
    {
537
        return isset($this->hotspot_type[$id]) ? $this->hotspot_type[$id] : null;
538
    }
539
540
    /**
541
     * Creates a new answer
542
     *
543
     * @author Olivier Brouckaert
544
     * @param string $answer answer title
545
     * @param integer $correct 0 if bad answer, not 0 if good answer
546
     * @param string $comment answer comment
547
     * @param integer $weighting answer weighting
548
     * @param integer $position answer position
549
     * @param array $new_hotspot_coordinates Coordinates for hotspot exercises (optional)
550
     * @param integer $new_hotspot_type Type for hotspot exercises (optional)
551
     * @param string $destination
552
     */
553
    public function createAnswer(
554
        $answer,
555
        $correct,
556
        $comment,
557
        $weighting,
558
        $position,
559
        $new_hotspot_coordinates = null,
560
        $new_hotspot_type = null,
561
        $destination = ''
562
    ) {
563
        $this->new_nbrAnswers++;
564
        $id = $this->new_nbrAnswers;
565
        $this->new_answer[$id] = $answer;
566
        $this->new_correct[$id] = $correct;
567
        $this->new_comment[$id] = $comment;
568
        $this->new_weighting[$id] = $weighting;
569
        $this->new_position[$id] = $position;
570
        $this->new_hotspot_coordinates[$id] = $new_hotspot_coordinates;
571
        $this->new_hotspot_type[$id] = $new_hotspot_type;
572
        $this->new_destination[$id] = $destination;
573
    }
574
575
    /**
576
     * Updates an answer
577
     *
578
     * @author Toon Keppens
579
     * @param int $iid
580
     * @param string $answer
581
     * @param string $comment
582
     * @param string $correct
583
     * @param string $weighting
584
     * @param string $position
585
     * @param string $destination
586
     * @param string $hotSpotCoordinates
587
     * @param string $hotSpotType
588
     *
589
     * @return CQuizAnswer
590
     */
591
    public function updateAnswers(
592
        $iid,
593
        $answer,
594
        $comment,
595
        $correct,
596
        $weighting,
597
        $position,
598
        $destination,
599
        $hotSpotCoordinates,
600
        $hotSpotType
601
    ) {
602
        $em = Database::getManager();
603
604
        /** @var CQuizAnswer $quizAnswer */
605
        $quizAnswer = $em->find('ChamiloCourseBundle:CQuizAnswer', $iid);
606
        if ($quizAnswer) {
607
            $quizAnswer
608
                ->setAnswer($answer)
609
                ->setComment($comment)
610
                ->setCorrect($correct)
611
                ->setPonderation($weighting)
612
                ->setPosition($position)
613
                ->setDestination($destination)
614
                ->setHotspotCoordinates($hotSpotCoordinates)
615
                ->setHotspotType($hotSpotType);
616
617
            $em->merge($quizAnswer);
618
            $em->flush();
619
620
            return $quizAnswer;
621
        }
622
623
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type Chamilo\CourseBundle\Entity\CQuizAnswer.
Loading history...
624
    }
625
626
    /**
627
     * Records answers into the data base
628
     *
629
     * @author Olivier Brouckaert
630
     */
631
    public function save()
632
    {
633
        $answerTable = Database::get_course_table(TABLE_QUIZ_ANSWER);
634
        $em = Database::getManager();
635
        $questionId = (int) $this->questionId;
636
637
        $courseId = $this->course['real_id'];
638
        $correctList = [];
639
        $answerList = [];
640
641
        for ($i = 1; $i <= $this->new_nbrAnswers; $i++) {
642
            $answer = $this->new_answer[$i];
643
            $correct = isset($this->new_correct[$i]) ? $this->new_correct[$i] : '';
644
            $comment = isset($this->new_comment[$i]) ? $this->new_comment[$i] : '';
645
            $weighting = isset($this->new_weighting[$i]) ? $this->new_weighting[$i] : '';
646
            $position = isset($this->new_position[$i]) ? $this->new_position[$i] : '';
647
            $hotspot_coordinates = isset($this->new_hotspot_coordinates[$i]) ? $this->new_hotspot_coordinates[$i] : '';
648
            $hotspot_type = isset($this->new_hotspot_type[$i]) ? $this->new_hotspot_type[$i] : '';
649
            $destination = isset($this->new_destination[$i]) ? $this->new_destination[$i] : '';
650
651
            $autoId = $this->selectAutoId($i);
652
            $iid = isset($this->iid[$i]) ? $this->iid[$i] : 0;
653
            $questionType = $this->getQuestionType();
654
655
            if (!isset($this->position[$i])) {
656
                $quizAnswer = new CQuizAnswer();
657
                $quizAnswer
658
                    ->setIdAuto($autoId)
659
                    ->setCId($courseId)
660
                    ->setQuestionId($questionId)
661
                    ->setAnswer($answer)
662
                    ->setCorrect($correct)
0 ignored issues
show
Bug introduced by
It seems like $correct can also be of type string; however, parameter $correct of Chamilo\CourseBundle\Ent...uizAnswer::setCorrect() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

662
                    ->setCorrect(/** @scrutinizer ignore-type */ $correct)
Loading history...
663
                    ->setComment($comment)
664
                    ->setPonderation($weighting)
0 ignored issues
show
Bug introduced by
It seems like $weighting can also be of type string; however, parameter $weight of Chamilo\CourseBundle\Ent...nswer::setPonderation() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

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

664
                    ->setPonderation(/** @scrutinizer ignore-type */ $weighting)
Loading history...
665
                    ->setPosition($position)
0 ignored issues
show
Bug introduced by
It seems like $position can also be of type string; however, parameter $position of Chamilo\CourseBundle\Ent...izAnswer::setPosition() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

665
                    ->setPosition(/** @scrutinizer ignore-type */ $position)
Loading history...
666
                    ->setHotspotCoordinates($hotspot_coordinates)
667
                    ->setHotspotType($hotspot_type)
668
                    ->setDestination($destination);
669
670
                $em->persist($quizAnswer);
671
                $em->flush();
672
673
                $iid = $quizAnswer->getIid();
674
675
676
                if ($iid) {
677
                    $quizAnswer
678
                        ->setId($iid)
679
                        ->setIdAuto($iid);
680
681
                    $em->merge($quizAnswer);
682
                    $em->flush();
683
684
                    if (in_array($questionType, [MATCHING, MATCHING_DRAGGABLE])) {
685
                        $answer = new Answer($this->questionId);
686
                        $answer->read();
687
                        $correctAnswerId = $answer->selectAnswerIdByPosition($correct);
688
689
                        // Continue to avoid matching question bug if $correctAnswerId returns false
690
                        // See : https://support.chamilo.org/issues/8334
691
                        if ($questionType == MATCHING && !$correctAnswerId) {
692
                            continue;
693
                        }
694
695
                        $correctAnswerAutoId = $answer->selectAutoId($correct);
696
                        $quizAnswer->setCorrect($correctAnswerAutoId ? $correctAnswerAutoId : 0);
697
698
                        $em->merge($quizAnswer);
699
                        $em->flush();
700
                    }
701
                }
702
            } else {
703
                // https://support.chamilo.org/issues/6558
704
                // function updateAnswers already escape_string, error if we do it twice.
705
                // Feed function updateAnswers with none escaped strings
706
                $this->updateAnswers(
707
                    $iid,
708
                    $this->new_answer[$i],
709
                    $this->new_comment[$i],
710
                    $this->new_correct[$i],
711
                    $this->new_weighting[$i],
712
                    $this->new_position[$i],
713
                    $this->new_destination[$i],
714
                    $this->new_hotspot_coordinates[$i],
715
                    $this->new_hotspot_type[$i]
716
                );
717
            }
718
719
            $answerList[$i] = $iid;
720
721
            if ($correct) {
722
                $correctList[$iid] = true;
723
            }
724
        }
725
726
        $questionType = 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

726
        /** @scrutinizer ignore-call */ 
727
        $questionType = self::getQuestionType();
Loading history...
727
728
        switch ($questionType) {
729
            case MATCHING_DRAGGABLE:
730
                foreach ($this->new_correct as $value => $status) {
731
                    if (!empty($status)) {
732
                        if (isset($answerList[$status])) {
733
                            $correct = $answerList[$status];
734
                        } else {
735
                            $correct = $status;
736
                        }
737
                        $myAutoId = $answerList[$value];
738
                        $sql = "UPDATE $answerTable
739
                            SET correct = '$correct'
740
                            WHERE
741
                                id_auto = $myAutoId
742
                            ";
743
                        Database::query($sql);
744
                    }
745
                }
746
                break;
747
            case DRAGGABLE:
748
                foreach ($this->new_correct as $value => $status) {
749
                    if (!empty($status)) {
750
                        $correct = $answerList[$status];
751
                        $myAutoId = $answerList[$value];
752
                        $sql = "UPDATE $answerTable
753
                            SET correct = '$correct'
754
                            WHERE
755
                                id_auto = $myAutoId
756
                            ";
757
                        Database::query($sql);
758
                    }
759
                }
760
                break;
761
        }
762
763
        if (count($this->position) > $this->new_nbrAnswers) {
764
            $i = $this->new_nbrAnswers + 1;
765
            while ($this->position[$i]) {
766
                $position = $this->position[$i];
767
                $sql = "DELETE FROM $answerTable
768
                        WHERE
769
                            c_id = {$this->course_id} AND
770
                            question_id = '".$questionId."' AND
771
                            position ='$position'";
772
                Database::query($sql);
773
                $i++;
774
            }
775
        }
776
777
        // moves $new_* arrays
778
        $this->answer = $this->new_answer;
779
        $this->correct = $this->new_correct;
780
        $this->comment = $this->new_comment;
781
        $this->weighting = $this->new_weighting;
782
        $this->position = $this->new_position;
783
        $this->hotspot_coordinates = $this->new_hotspot_coordinates;
784
        $this->hotspot_type = $this->new_hotspot_type;
785
        $this->nbrAnswers = $this->new_nbrAnswers;
786
        $this->destination = $this->new_destination;
787
788
        $this->cancel();
789
    }
790
791
    /**
792
     * Duplicates answers by copying them into another question
793
     *
794
     * @author Olivier Brouckaert
795
     * @param  Question $newQuestion
796
     * @param  array $course_info destination course info (result of the function api_get_course_info() )
797
     */
798
    public function duplicate($newQuestion, $course_info = null)
799
    {
800
        $newQuestionId = $newQuestion->id;
801
802
        if (empty($course_info)) {
803
            $course_info = $this->course;
804
        }
805
806
        $fixed_list = [];
807
        $tableAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
808
809
        if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE ||
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

809
        if (self::/** @scrutinizer ignore-call */ getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE ||
Loading history...
810
            self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE
811
        ) {
812
            // Selecting origin options
813
            $origin_options = Question::readQuestionOption(
814
                $this->selectQuestionId(),
815
                $this->course['real_id']
816
            );
817
818
            if (!empty($origin_options)) {
819
                foreach ($origin_options as $item) {
820
                    $new_option_list[] = $item['id'];
821
                }
822
            }
823
824
            $destination_options = Question::readQuestionOption(
825
                $newQuestionId,
826
                $course_info['real_id']
827
            );
828
            $i = 0;
829
            if (!empty($destination_options)) {
830
                foreach ($destination_options as $item) {
831
                    $fixed_list[$new_option_list[$i]] = $item['id'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $new_option_list does not seem to be defined for all execution paths leading up to this point.
Loading history...
832
                    $i++;
833
                }
834
            }
835
        }
836
837
        // if at least one answer
838
        if ($this->nbrAnswers) {
839
            // inserts new answers into data base
840
            $courseId = $course_info['real_id'];
841
            $correctAnswers = [];
842
            $onlyAnswers = [];
843
            $allAnswers = [];
844
845
            $em = Database::getManager();
846
847
            if (in_array($newQuestion->type, [MATCHING, MATCHING_DRAGGABLE])) {
848
                $temp = [];
849
                for ($i = 1; $i <= $this->nbrAnswers; $i++) {
850
                    $answer = [
851
                        'id' => $this->id[$i],
852
                        'answer' => $this->answer[$i],
853
                        'correct' => $this->correct[$i],
854
                        'comment' => $this->comment[$i],
855
                        'weighting' => $this->weighting[$i],
856
                        'ponderation' => $this->weighting[$i],
857
                        'position' => $this->position[$i],
858
                        'hotspot_coordinates' => $this->hotspot_coordinates[$i],
859
                        'hotspot_type' => $this->hotspot_type[$i],
860
                        'destination' => $this->destination[$i],
861
                    ];
862
                    $temp[$answer['position']] = $answer;
863
                    $allAnswers[$this->id[$i]] = $this->answer[$i];
864
                }
865
866
                foreach ($temp as $index => $answer) {
867
                    if ($this->course['id'] != $course_info['id']) {
868
                        // check resources inside html from ckeditor tool and copy correct urls into recipient course
869
                        $answer['answer'] = DocumentManager::replaceUrlWithNewCourseCode(
870
                            $answer['answer'],
871
                            $this->course['id'],
872
                            $course_info['id']
873
                        );
874
875
                        $answer['comment'] = DocumentManager::replaceUrlWithNewCourseCode(
876
                            $answer['comment'],
877
                            $this->course['id'],
878
                            $course_info['id']
879
                        );
880
                    }
881
882
                    $quizAnswer = new CQuizAnswer();
883
                    $quizAnswer
884
                        ->setCId($courseId)
885
                        ->setQuestionId($newQuestionId)
886
                        ->setAnswer($answer['answer'])
887
                        ->setCorrect($answer['correct'])
888
                        ->setComment($answer['comment'])
889
                        ->setPonderation($answer['ponderation'])
890
                        ->setPosition($answer['position'])
891
                        ->setHotspotCoordinates($answer['hotspot_coordinates'])
892
                        ->setHotspotType($answer['hotspot_type'])
893
                        ->setIdAuto(0);
894
895
                    $em->persist($quizAnswer);
896
                    $em->flush();
897
898
                    $answerId = $quizAnswer->getIid();
899
900
                    if ($answerId) {
901
                        $quizAnswer
902
                            ->setId($answerId)
903
                            ->setIdAuto($answerId);
904
905
                        $em->merge($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 ($this->course['id'] != $course_info['id']) {
915
                        $this->answer[$i] = DocumentManager::replaceUrlWithNewCourseCode(
916
                            $this->answer[$i],
917
                            $this->course['id'],
918
                            $course_info['id']
919
                        );
920
                        $this->comment[$i] = DocumentManager::replaceUrlWithNewCourseCode(
921
                            $this->comment[$i],
922
                            $this->course['id'],
923
                            $course_info['id']
924
                        );
925
                    }
926
927
                    $correct = $this->correct[$i];
928
                    if ($newQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE ||
929
                        $newQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE
930
                    ) {
931
                        $correct = $fixed_list[intval($correct)];
932
                    }
933
934
                    $quizAnswer = new CQuizAnswer();
935
                    $quizAnswer
936
                        ->setCId($courseId)
937
                        ->setQuestionId($newQuestionId)
938
                        ->setAnswer($this->answer[$i])
939
                        ->setCorrect($correct)
940
                        ->setComment($this->comment[$i])
941
                        ->setPonderation($this->weighting[$i])
942
                        ->setPosition($this->position[$i])
943
                        ->setHotspotCoordinates($this->hotspot_coordinates[$i])
944
                        ->setHotspotType($this->hotspot_type[$i])
945
                        ->setDestination($this->destination[$i]);
946
947
                    $em->persist($quizAnswer);
948
                    $em->flush();
949
950
                    $answerId = $quizAnswer->getIid();
951
                    $quizAnswer
952
                        ->setId($answerId)
953
                        ->setIdAuto($answerId);
954
955
                    $em->merge($quizAnswer);
956
                    $em->flush();
957
958
                    $correctAnswers[$answerId] = $correct;
959
                    $onlyAnswers[$answerId] = $this->answer[$i];
960
                    $allAnswers[$this->id[$i]] = $this->answer[$i];
961
                }
962
            }
963
964
            // Fix correct answers
965
            if (in_array($newQuestion->type, [DRAGGABLE, MATCHING, MATCHING_DRAGGABLE])) {
966
                $onlyAnswersFlip = array_flip($onlyAnswers);
967
                foreach ($correctAnswers as $answer_id => $correct_answer) {
968
                    $params = [];
969
                    if (isset($allAnswers[$correct_answer]) &&
970
                        isset($onlyAnswersFlip[$allAnswers[$correct_answer]])
971
                    ) {
972
                        $params['correct'] = $onlyAnswersFlip[$allAnswers[$correct_answer]];
973
                        Database::update(
974
                            $tableAnswer,
975
                            $params,
976
                            [
977
                                'id = ? AND c_id = ? AND question_id = ? ' => [
978
                                    $answer_id,
979
                                    $courseId,
980
                                    $newQuestionId,
981
                                ],
982
                            ]
983
                        );
984
                    }
985
                }
986
            }
987
        }
988
    }
989
990
    /**
991
     * Get the necessary JavaScript for some answers
992
     * @return string
993
     */
994
    public function getJs()
995
    {
996
        //if ($this->questionId == 2)
997
        return "<script>
998
                jsPlumb.ready(function() {
999
                    if ($('#drag{$this->questionId}_question').length > 0) {
1000
                        MatchingDraggable.init('{$this->questionId}');
1001
                    }
1002
                });
1003
            </script>";
1004
    }
1005
1006
    /**
1007
     * Check if a answer is correct by an answer auto id
1008
     * @param $needle int The answer auto id
1009
     * @return bool
1010
     */
1011
    public function isCorrectByAutoId($needle)
1012
    {
1013
        $key = 0;
1014
        foreach ($this->autoId as $autoIdKey => $autoId) {
1015
            if ($autoId == $needle) {
1016
                $key = $autoIdKey;
1017
            }
1018
        }
1019
1020
        if (!$key) {
1021
            return false;
1022
        }
1023
1024
        return $this->isCorrect($key) ? true : false;
1025
    }
1026
}
1027