Passed
Push — master ( e324b4...64b3aa )
by
unknown
09:12
created

AbstractLink::getSkillsFromItem()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 30
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 25
nc 8
nop 0
dl 0
loc 30
rs 8.4444
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\GradebookCategory;
5
use Chamilo\CoreBundle\Entity\GradebookLink;
6
7
/**
8
 * Class AbstractLink
9
 * Defines a gradebook AbstractLink object.
10
 * To implement specific links,
11
 * extend this class and define a type in LinkFactory.
12
 * Use the methods in LinkFactory to create link objects.
13
 *
14
 * @author Bert Steppé
15
 * @author Julio Montoya <[email protected]> security improvements
16
 */
17
abstract class AbstractLink implements GradebookItem
18
{
19
    public $course_id;
20
    public $studentList;
21
    /** @var GradebookLink */
22
    public $entity;
23
    protected $id;
24
    protected $type;
25
    protected $ref_id;
26
    protected $user_id;
27
    protected $course_code;
28
    /** @var Category */
29
    protected $category;
30
    protected $created_at;
31
    protected $weight;
32
    protected $visible;
33
    protected $session_id;
34
35
    /**
36
     * Constructor.
37
     */
38
    public function __construct()
39
    {
40
        $this->course_id = api_get_course_int_id();
41
    }
42
43
    /**
44
     * @return bool
45
     */
46
    abstract public function has_results();
47
48
    /**
49
     * @return string
50
     */
51
    abstract public function get_link();
52
53
    /**
54
     * @return bool
55
     */
56
    abstract public function is_valid_link();
57
58
    /**
59
     * @return string
60
     */
61
    abstract public function get_type_name();
62
63
    /**
64
     * @return bool
65
     */
66
    abstract public function needs_name_and_description();
67
68
    /**
69
     * @return bool
70
     */
71
    abstract public function needs_max();
72
73
    /**
74
     * @return bool
75
     */
76
    abstract public function needs_results();
77
78
    /**
79
     * @return bool
80
     */
81
    abstract public function is_allowed_to_change_name();
82
83
    /**
84
     * @return int
85
     */
86
    public function get_id()
87
    {
88
        return $this->id;
89
    }
90
91
    /**
92
     * @return string
93
     */
94
    public function get_type()
95
    {
96
        return $this->type;
97
    }
98
99
    /**
100
     * @return int
101
     */
102
    public function get_ref_id()
103
    {
104
        return (int) $this->ref_id;
105
    }
106
107
    /**
108
     * @return int
109
     */
110
    public function get_session_id()
111
    {
112
        return (int) $this->session_id;
113
    }
114
115
    /**
116
     * @return int
117
     */
118
    public function get_user_id()
119
    {
120
        return $this->user_id;
121
    }
122
123
    /**
124
     * @return string
125
     */
126
    public function get_course_code()
127
    {
128
        return $this->course_code;
129
    }
130
131
    /**
132
     * @return Category
133
     */
134
    public function getCategory()
135
    {
136
        return $this->category;
137
    }
138
139
    /**
140
     * @param Category $category
141
     */
142
    public function setCategory($category)
143
    {
144
        $this->category = $category;
145
    }
146
147
    /**
148
     * @return int
149
     */
150
    public function get_category_id()
151
    {
152
        return $this->category->get_id();
153
    }
154
155
    /**
156
     * @param int $category_id
157
     */
158
    public function set_category_id($category_id)
159
    {
160
        $categories = Category::load($category_id);
161
        if (isset($categories[0])) {
162
            $this->setCategory($categories[0]);
163
        }
164
    }
165
166
    public function get_date()
167
    {
168
        return $this->created_at;
169
    }
170
171
    public function get_weight()
172
    {
173
        return $this->weight;
174
    }
175
176
    public function is_locked()
177
    {
178
        return isset($this->locked) && 1 == $this->locked ? true : false;
179
    }
180
181
    public function is_visible()
182
    {
183
        return $this->visible;
184
    }
185
186
    public function set_id($id)
187
    {
188
        $this->id = $id;
189
    }
190
191
    public function set_type($type)
192
    {
193
        $this->type = $type;
194
    }
195
196
    public function set_ref_id($ref_id)
197
    {
198
        $this->ref_id = $ref_id;
199
    }
200
201
    public function set_user_id($user_id)
202
    {
203
        $this->user_id = $user_id;
204
    }
205
206
    /**
207
     * Set course ID and course code. If ID is empty, set both to null
208
     * @param ?int $courseId
209
     */
210
    public function setCourseId(?int $courseId = null): AbstractLink {
211
        $courseInfo = api_get_course_info_by_id($courseId);
212
        if (!empty($courseInfo)) {
213
            $this->course_code = $courseInfo['code'];
214
            $this->course_id = $courseId;
215
        } else {
216
            $this->course_code = null;
217
            $this->course_id = null;
218
        }
219
220
        return $this;
221
    }
222
223
    /**
224
     * @return array
225
     */
226
    public function getStudentList()
227
    {
228
        if (empty($this->studentList)) {
229
            return [];
230
        }
231
232
        return $this->studentList;
233
    }
234
235
    /**
236
     * @param array $list
237
     */
238
    public function setStudentList($list)
239
    {
240
        $this->studentList = $list;
241
    }
242
243
    public function set_date($date)
244
    {
245
        $this->created_at = $date;
246
    }
247
248
    public function set_weight($weight)
249
    {
250
        $this->weight = $weight;
251
    }
252
253
    public function set_visible($visible)
254
    {
255
        $this->visible = $visible;
256
    }
257
258
    /**
259
     * @param int $id
260
     */
261
    public function set_session_id($id)
262
    {
263
        $this->session_id = $id;
264
    }
265
266
    /**
267
     * @param $locked
268
     */
269
    public function set_locked($locked)
270
    {
271
        $this->locked = $locked;
272
    }
273
274
    /**
275
     * @return int
276
     */
277
    public function getCourseId()
278
    {
279
        return (int) $this->course_id;
280
    }
281
282
    /**
283
     * Retrieve links and return them as an array of extensions of AbstractLink.
284
     * To keep consistency, do not call this method but LinkFactory::load instead.
285
     *
286
     * @param int    $id
287
     * @param int    $type
288
     * @param int    $ref_id
289
     * @param int    $user_id
290
     * @param ?int    $courseId
291
     * @param int    $category_id
292
     * @param int    $visible
293
     *
294
     * @return array
295
     */
296
    public static function load(
297
        $id = null,
298
        $type = null,
299
        $ref_id = null,
300
        $user_id = null,
301
        ?int $courseId = null,
302
        $category_id = null,
303
        $visible = null
304
    ) {
305
        $tbl_grade_links = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
306
        $sql = 'SELECT * FROM '.$tbl_grade_links;
307
        $paramcount = 0;
308
        if (isset($id)) {
309
            $sql .= ' WHERE id = '.intval($id);
310
            $paramcount++;
311
        }
312
        if (isset($type)) {
313
            if (0 != $paramcount) {
314
                $sql .= ' AND';
315
            } else {
316
                $sql .= ' WHERE';
317
            }
318
            $sql .= ' type = '.intval($type);
319
            $paramcount++;
320
        }
321
        if (isset($ref_id)) {
322
            if (0 != $paramcount) {
323
                $sql .= ' AND';
324
            } else {
325
                $sql .= ' WHERE';
326
            }
327
            $sql .= ' ref_id = '.intval($ref_id);
328
            $paramcount++;
329
        }
330
        if (isset($user_id)) {
331
            if (0 != $paramcount) {
332
                $sql .= ' AND';
333
            } else {
334
                $sql .= ' WHERE';
335
            }
336
            $sql .= ' user_id = '.intval($user_id);
337
            $paramcount++;
338
        }
339
        if (!empty($courseId)) {
340
            if (0 != $paramcount) {
341
                $sql .= ' AND';
342
            } else {
343
                $sql .= ' WHERE';
344
            }
345
            $sql .= " c_id = $courseId";
346
            $paramcount++;
347
        }
348
        if (isset($category_id)) {
349
            if (0 != $paramcount) {
350
                $sql .= ' AND';
351
            } else {
352
                $sql .= ' WHERE';
353
            }
354
            $sql .= ' category_id = '.intval($category_id);
355
            $paramcount++;
356
        }
357
        if (isset($visible)) {
358
            if (0 != $paramcount) {
359
                $sql .= ' AND';
360
            } else {
361
                $sql .= ' WHERE';
362
            }
363
            $sql .= ' visible = '.intval($visible);
364
        }
365
366
        $result = Database::query($sql);
367
        $links = self::create_objects_from_sql_result($result);
368
369
        $em = Database::getManager();
370
        $repo = $em->getRepository(GradebookLink::class);
371
372
        foreach ($links as $link) {
373
            $link->entity = $repo->find($link->get_id());
374
        }
375
376
        return $links;
377
    }
378
379
    /**
380
     * Insert this link into the database.
381
     * @return int 0 on error, or the link's database id
382
     * @throws Exception
383
     */
384
    public function add(): int
385
    {
386
        $this->add_linked_data();
387
        if (!empty($this->type) &&
388
            !empty($this->ref_id) &&
389
            !empty($this->course_id) &&
390
            !empty($this->category)
391
        ) {
392
            $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
393
            $sql = "SELECT count(*) count FROM $table
394
                    WHERE
395
                        ref_id = ".$this->get_ref_id()." AND
396
                        category_id =  ".$this->category->get_id()." AND
397
                        c_id = '".$this->course_id."' AND
398
                        type =  ".$this->type." ";
399
400
            $result = Database::query($sql);
401
            $row = Database::fetch_assoc($result);
402
403
            if (0 == $row['count']) {
404
                $em = Database::getManager();
405
                $category = null;
406
                if (!empty($this->get_category_id())) {
407
                    $category = $em->getRepository(GradebookCategory::class)->find($this->get_category_id());
408
                }
409
410
                $link = new GradebookLink();
411
                $link
412
                    ->setType($this->get_type())
413
                    ->setVisible($this->is_visible())
414
                    ->setWeight(api_float_val($this->get_weight()))
415
                    ->setRefId($this->get_ref_id())
416
                    ->setCategory($category)
417
                    ->setCourse(api_get_course_entity($this->course_id))
418
                ;
419
                $em->persist($link);
420
                $em->flush();
421
422
                $this->set_id($link->getId());
423
424
                Event::addEvent(
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

424
                Event::/** @scrutinizer ignore-call */ 
425
                       addEvent(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
425
                    'gradebook_link_created',
426
                    'link',
427
                    $link->getId(),
428
                    null,
429
                    api_get_user_id(),
430
                    $this->course_id
431
                );
432
433
                return $link->getId();
434
            }
435
        }
436
437
        return 0;
438
    }
439
440
    /**
441
     * Update the properties of this link in the database.
442
     */
443
    public function save()
444
    {
445
        $em = Database::getManager();
446
        $link = $em->find(GradebookLink::class, $this->id);
447
448
        if (!$link) {
449
            return;
450
        }
451
452
        self::add_link_log($this->id);
453
        $this->save_linked_data();
454
        $course = api_get_course_entity($this->getCourseId());
455
456
        $category = null;
457
        if (!empty($this->get_category_id())) {
458
            $category = $em->getRepository(GradebookCategory::class)->find($this->get_category_id());
459
        }
460
461
        $link
462
            ->setType($this->get_type())
463
            ->setRefId($this->get_ref_id())
464
            ->setCourse($course)
465
            ->setCategory($category)
466
            ->setWeight($this->get_weight())
467
            ->setVisible($this->is_visible())
468
        ;
469
470
        $em->persist($link);
471
        $em->flush();
472
    }
473
474
    /**
475
     * @param int $evaluationId
476
     */
477
    public static function add_link_log($evaluationId, $nameLog = null)
478
    {
479
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINKEVAL_LOG);
480
        $dateobject = self::load($evaluationId, null, null, null, null);
481
        $now = api_get_utc_datetime();
482
        $arreval = get_object_vars($dateobject[0]);
483
        $description_log = isset($arreval['description']) ? $arreval['description'] : '';
484
        if (empty($nameLog)) {
485
            if (isset($_POST['name_link'])) {
486
                $name_log = isset($_POST['name_link']) ? $_POST['name_link'] : $arreval['course_id'];
487
            } elseif (isset($_POST['link_'.$evaluationId]) && $_POST['link_'.$evaluationId]) {
488
                $name_log = $_POST['link_'.$evaluationId];
489
            } else {
490
                $name_log = $arreval['course_id'];
491
            }
492
        } else {
493
            $name_log = $nameLog;
494
        }
495
496
        $params = [
497
            'id_linkeval_log' => $arreval['id'],
498
            'title' => $name_log,
499
            'description' => $description_log,
500
            'created_at' => $now,
501
            'weight' => $arreval['weight'],
502
            'visible' => $arreval['visible'],
503
            'type' => 'Link',
504
            'user_id_log' => api_get_user_id(),
505
        ];
506
        Database::insert($table, $params);
507
    }
508
509
    /**
510
     * Delete this link from the database.
511
     */
512
    public function delete()
513
    {
514
        $this->delete_linked_data();
515
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
516
        $sql = 'DELETE FROM '.$table.'
517
                WHERE id = '.intval($this->id);
518
        Database::query($sql);
519
    }
520
521
    /**
522
     * Generate an array of possible categories where this link can be moved to.
523
     * Notice: its own parent will be included in the list: it's up to the frontend
524
     * to disable this element.
525
     *
526
     * @return array 2-dimensional array - every element contains 3 subelements (id, name, level)
527
     */
528
    public function get_target_categories()
529
    {
530
        // links can only be moved to categories inside this course
531
        $targets = [];
532
        $level = 0;
533
        $categories = Category::load(null, null, $this->course_id, 0);
534
        foreach ($categories as $cat) {
535
            $targets[] = [$cat->get_id(), $cat->get_name(), $level + 1];
536
            $targets = $this->addTargetSubcategories(
537
                $targets,
538
                $level + 1,
539
                $cat->get_id()
540
            );
541
        }
542
543
        return $targets;
544
    }
545
546
    /**
547
     * Move this link to the given category.
548
     * If this link moves to outside a course, delete it.
549
     */
550
    public function move_to_cat(GradebookCategory $cat)
551
    {
552
        if ($this->getCourseId() != $cat->getCourse()->getId()) {
553
            $this->delete();
554
        } else {
555
            $this->set_category_id($cat->getId());
556
            $this->save();
557
        }
558
    }
559
560
    /**
561
     * Find links by name
562
     * To keep consistency, do not call this method but LinkFactory::find_links instead.
563
     *
564
     * @todo can be written more efficiently using a new (but very complex) sql query
565
     *
566
     * @param string $name_mask
567
     *
568
     * @return array
569
     */
570
    public static function find_links($name_mask, $selectcat)
571
    {
572
        $rootcat = Category::load($selectcat);
573
        $links = $rootcat[0]->get_links((api_is_allowed_to_edit() ? null : api_get_user_id()), true);
574
        $foundlinks = [];
575
        foreach ($links as $link) {
576
            if (!(false === api_strpos(api_strtolower($link->get_name()), api_strtolower($name_mask)))) {
577
                $foundlinks[] = $link;
578
            }
579
        }
580
581
        return $foundlinks;
582
    }
583
584
    /**
585
     * @return string
586
     */
587
    public function get_item_type()
588
    {
589
        return 'L';
590
    }
591
592
    /**
593
     * @return string
594
     */
595
    public function get_icon_name()
596
    {
597
        return 'link';
598
    }
599
600
    public function get_all_links()
601
    {
602
        return [];
603
    }
604
605
    public function add_linked_data()
606
    {
607
    }
608
609
    public function save_linked_data()
610
    {
611
    }
612
613
    public function delete_linked_data()
614
    {
615
    }
616
617
    /**
618
     * @param string $name
619
     */
620
    public function set_name($name)
621
    {
622
    }
623
624
    /**
625
     * @param string $description
626
     */
627
    public function set_description($description)
628
    {
629
    }
630
631
    /**
632
     * @param int $max
633
     */
634
    public function set_max($max)
635
    {
636
    }
637
638
    public function get_view_url($stud_id)
639
    {
640
        return null;
641
    }
642
643
    /**
644
     * Locks a link.
645
     *
646
     * @param int $locked 1 or unlocked 0
647
     *
648
     * */
649
    public function lock($locked)
650
    {
651
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
652
        $sql = "UPDATE $table SET locked = '".intval($locked)."'
653
                WHERE id='".$this->id."'";
654
        Database::query($sql);
655
    }
656
657
    /**
658
     * Get current user ranking.
659
     *
660
     * @param int   $userId
661
     * @param array $studentList Array with user id and scores
662
     *                           Example: [1 => 5.00, 2 => 8.00]
663
     *
664
     * @return array
665
     */
666
    public static function getCurrentUserRanking($userId, $studentList)
667
    {
668
        $previousScore = null;
669
        $ranking = null;
670
        $position = null;
671
        $currentUserId = $userId;
672
        if (!empty($studentList) && !empty($currentUserId)) {
673
            $studentList = array_map('floatval', $studentList);
674
            arsort($studentList);
675
            $count = count($studentList);
676
            foreach ($studentList as $userId => $score) {
0 ignored issues
show
introduced by
$userId is overwriting one of the parameters of this function.
Loading history...
677
                $position++;
678
                if ($previousScore === null || $score < $previousScore) {
679
                    $ranking = $position;
680
                }
681
                $previousScore = $score;
682
                if ($currentUserId == $userId) {
683
                    break;
684
                }
685
            }
686
687
            // If no ranking was detected.
688
            if (0 == $ranking) {
689
                return [];
690
            }
691
692
            return [$ranking, $count];
693
        }
694
695
        return [];
696
    }
697
698
    /**
699
     * @return string
700
     */
701
    public function getSkillsFromItem()
702
    {
703
        $toolType = '';
704
        switch ($this->type) {
705
            case LINK_ATTENDANCE:
706
                $toolType = ITEM_TYPE_ATTENDANCE;
707
                break;
708
            case LINK_EXERCISE:
709
                $toolType = ITEM_TYPE_EXERCISE;
710
                break;
711
            case LINK_FORUM_THREAD:
712
                $toolType = ITEM_TYPE_FORUM_THREAD;
713
                break;
714
            case LINK_LEARNPATH:
715
                $toolType = ITEM_TYPE_LEARNPATH;
716
                break;
717
            case LINK_HOTPOTATOES:
718
                $toolType = ITEM_TYPE_HOTPOTATOES;
719
                break;
720
            case LINK_STUDENTPUBLICATION:
721
                $toolType = ITEM_TYPE_STUDENT_PUBLICATION;
722
                break;
723
            case LINK_SURVEY:
724
                $toolType = ITEM_TYPE_SURVEY;
725
                break;
726
        }
727
728
        $skillToString = SkillModel::getSkillRelItemsToString($toolType, $this->get_ref_id());
729
730
        return $skillToString;
731
    }
732
733
    /**
734
     * @param int $itemId
735
     * @param int $linkType
736
     * @param int $courseId
737
     * @param int $sessionId
738
     *
739
     * @return array
740
     * @throws Exception
741
     */
742
    public static function getGradebookLinksFromItem(
743
        int $itemId,
744
        int $linkType,
745
        int $courseId,
746
        ?int $sessionId = 0
747
    ): array
748
    {
749
        if (empty($courseId) || empty($itemId) || empty($linkType)) {
750
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the type-hinted return array.
Loading history...
751
        }
752
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
753
        $tableCategory = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
754
        $sessionId = (int) $sessionId;
755
756
        $sessionCondition = api_get_session_condition($sessionId, true, false, 'c.session_id');
757
758
        $sql = "SELECT DISTINCT l.*
759
                FROM $table l INNER JOIN $tableCategory c
760
                ON (c.c_id = l.c_id AND c.id = l.category_id)
761
                WHERE
762
                    ref_id = $itemId AND
763
                    type = $linkType AND
764
                    l.c_id = $courseId
765
                    $sessionCondition ";
766
767
        $result = Database::query($sql);
768
        if (Database::num_rows($result)) {
769
            return Database::store_result($result);
770
        }
771
772
        return [];
773
    }
774
775
    private static function create_objects_from_sql_result(\Doctrine\DBAL\Result $result): array
776
    {
777
        $links = [];
778
        $allow = ('true' === api_get_setting('gradebook.allow_gradebook_stats'));
779
        if ($allow) {
780
            $em = Database::getManager();
781
            $repo = $em->getRepository(GradebookLink::class);
782
        }
783
784
        while ($data = Database::fetch_array($result)) {
785
            $link = LinkFactory::create($data['type']);
786
            $link->set_id($data['id']);
787
            $link->set_type($data['type']);
788
            $link->set_ref_id($data['ref_id']);
789
            $link->setCourseId($data['c_id']);
790
            $link->set_category_id($data['category_id']);
791
            $link->set_date($data['created_at']);
792
            $link->set_weight($data['weight']);
793
            $link->set_visible($data['visible']);
794
            $link->set_locked($data['locked']);
795
796
            //session id should depend on the category --> $data['category_id']
797
            $session_id = api_get_session_id();
798
            $link->set_session_id($session_id);
799
800
            if ($allow) {
801
                $link->entity = $repo->find($data['id']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $repo does not seem to be defined for all execution paths leading up to this point.
Loading history...
802
            }
803
            $links[] = $link;
804
        }
805
806
        return $links;
807
    }
808
809
    /**
810
     * Internal function used by get_target_categories().
811
     *
812
     * @param array $targets
813
     * @param int   $level
814
     * @param int   $catid
815
     *
816
     * @return array
817
     */
818
    private function addTargetSubcategories($targets, $level, $catid)
819
    {
820
        $subcats = Category::load(null, null, 0, $catid);
821
        foreach ($subcats as $cat) {
822
            $targets[] = [$cat->get_id(), $cat->get_name(), $level + 1];
823
            $targets = $this->addTargetSubcategories(
824
                $targets,
825
                $level + 1,
826
                $cat->get_id()
827
            );
828
        }
829
830
        return $targets;
831
    }
832
}
833