Completed
Push — 1.11.x ( 9d008e...684266 )
by Julito
22:01 queued 09:23
created

Category::getGenerateCertificates()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\GradebookCategory;
6
use ChamiloSession as Session;
7
8
/**
9
 * Class Category
10
 * Defines a gradebook Category object.
11
 */
12
class Category implements GradebookItem
13
{
14
    public $studentList;
15
    public $evaluations;
16
    public $links;
17
    public $subCategories;
18
    /** @var GradebookCategory */
19
    public $entity;
20
    private $id;
21
    private $name;
22
    private $description;
23
    private $user_id;
24
    private $course_code;
25
    private $parent;
26
    private $weight;
27
    private $visible;
28
    private $certificate_min_score;
29
    private $session_id;
30
    private $skills = [];
31
    private $grade_model_id;
32
    private $generateCertificates;
33
    private $isRequirement;
34
    private $courseDependency;
35
    private $minimumToValidate;
36
    private $documentId;
37
    /** @var int */
38
    private $gradeBooksToValidateInDependence;
39
40
    /**
41
     * Consctructor.
42
     */
43
    public function __construct()
44
    {
45
        $this->id = 0;
46
        $this->name = null;
47
        $this->description = null;
48
        $this->user_id = 0;
49
        $this->course_code = null;
50
        $this->parent = 0;
51
        $this->weight = 0;
52
        $this->visible = false;
53
        $this->certificate_min_score = 0;
54
        $this->session_id = 0;
55
        $this->grade_model_id = 0;
56
        $this->generateCertificates = false;
57
        $this->isRequirement = false;
58
        $this->courseDependency = [];
59
        $this->documentId = 0;
60
        $this->minimumToValidate = null;
61
    }
62
63
    /**
64
     * @return int
65
     */
66
    public function get_id()
67
    {
68
        return $this->id;
69
    }
70
71
    /**
72
     * @return string
73
     */
74
    public function get_name()
75
    {
76
        return $this->name;
77
    }
78
79
    /**
80
     * @return string
81
     */
82
    public function get_description()
83
    {
84
        return $this->description;
85
    }
86
87
    /**
88
     * @return int
89
     */
90
    public function get_user_id()
91
    {
92
        return $this->user_id;
93
    }
94
95
    /**
96
     * @return int|null
97
     */
98
    public function getCertificateMinScore()
99
    {
100
        if (!empty($this->certificate_min_score)) {
101
            return $this->certificate_min_score;
102
        }
103
104
        return null;
105
    }
106
107
    /**
108
     * @return string
109
     */
110
    public function get_course_code()
111
    {
112
        return $this->course_code;
113
    }
114
115
    /**
116
     * @return int
117
     */
118
    public function get_parent_id()
119
    {
120
        return $this->parent;
121
    }
122
123
    /**
124
     * @return int
125
     */
126
    public function get_weight()
127
    {
128
        return $this->weight;
129
    }
130
131
    /**
132
     * @return bool
133
     */
134
    public function is_locked()
135
    {
136
        return isset($this->locked) && $this->locked == 1 ? true : false;
137
    }
138
139
    /**
140
     * @return bool
141
     */
142
    public function is_visible()
143
    {
144
        return $this->visible;
145
    }
146
147
    /**
148
     * Get $isRequirement.
149
     *
150
     * @return int
151
     */
152
    public function getIsRequirement()
153
    {
154
        return $this->isRequirement;
155
    }
156
157
    /**
158
     * @param int $id
159
     */
160
    public function set_id($id)
161
    {
162
        $this->id = $id;
163
    }
164
165
    /**
166
     * @param string $name
167
     */
168
    public function set_name($name)
169
    {
170
        $this->name = $name;
171
    }
172
173
    /**
174
     * @param string $description
175
     */
176
    public function set_description($description)
177
    {
178
        $this->description = $description;
179
    }
180
181
    /**
182
     * @param int $user_id
183
     */
184
    public function set_user_id($user_id)
185
    {
186
        $this->user_id = $user_id;
187
    }
188
189
    /**
190
     * @param string $course_code
191
     */
192
    public function set_course_code($course_code)
193
    {
194
        $this->course_code = $course_code;
195
    }
196
197
    /**
198
     * @param float $min_score
199
     */
200
    public function set_certificate_min_score($min_score = null)
201
    {
202
        $this->certificate_min_score = $min_score;
203
    }
204
205
    /**
206
     * @param int $parent
207
     */
208
    public function set_parent_id($parent)
209
    {
210
        $this->parent = (int) $parent;
211
    }
212
213
    /**
214
     * Filters to int and sets the session ID.
215
     *
216
     * @param   int     The session ID from the Dokeos course session
217
     */
218
    public function set_session_id($session_id = 0)
219
    {
220
        $this->session_id = (int) $session_id;
221
    }
222
223
    /**
224
     * @param $weight
225
     */
226
    public function set_weight($weight)
227
    {
228
        $this->weight = $weight;
229
    }
230
231
    /**
232
     * @param $visible
233
     */
234
    public function set_visible($visible)
235
    {
236
        $this->visible = $visible;
237
    }
238
239
    /**
240
     * @param int $id
241
     */
242
    public function set_grade_model_id($id)
243
    {
244
        $this->grade_model_id = $id;
245
    }
246
247
    /**
248
     * @param $locked
249
     */
250
    public function set_locked($locked)
251
    {
252
        $this->locked = $locked;
253
    }
254
255
    /**
256
     * Set $isRequirement.
257
     *
258
     * @param int $isRequirement
259
     */
260
    public function setIsRequirement($isRequirement)
261
    {
262
        $this->isRequirement = $isRequirement;
263
    }
264
265
    /**
266
     * @param $value
267
     */
268
    public function setCourseListDependency($value)
269
    {
270
        $this->courseDependency = [];
271
272
        $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
273
274
        if (false !== $unserialized) {
275
            $this->courseDependency = $unserialized;
276
        }
277
    }
278
279
    /**
280
     * Course id list.
281
     *
282
     * @return array
283
     */
284
    public function getCourseListDependency()
285
    {
286
        return $this->courseDependency;
287
    }
288
289
    /**
290
     * @param int $value
291
     */
292
    public function setMinimumToValidate($value)
293
    {
294
        $this->minimumToValidate = $value;
295
    }
296
297
    public function getMinimumToValidate()
298
    {
299
        return $this->minimumToValidate;
300
    }
301
302
    /**
303
     * @return int|null
304
     */
305
    public function get_grade_model_id()
306
    {
307
        if ($this->grade_model_id < 0) {
308
            return null;
309
        }
310
311
        return $this->grade_model_id;
312
    }
313
314
    /**
315
     * @return string
316
     */
317
    public function get_type()
318
    {
319
        return 'category';
320
    }
321
322
    /**
323
     * @param bool $from_db
324
     *
325
     * @return array|resource
326
     */
327
    public function get_skills($from_db = true)
328
    {
329
        if ($from_db) {
330
            $categoryId = $this->get_id();
331
            $gradebook = new Gradebook();
332
            $skills = $gradebook->getSkillsByGradebook($categoryId);
333
        } else {
334
            $skills = $this->skills;
335
        }
336
337
        return $skills;
338
    }
339
340
    /**
341
     * @return array
342
     */
343
    public function getSkillsForSelect()
344
    {
345
        $skills = $this->get_skills();
346
        $skill_select = [];
347
        if (!empty($skills)) {
348
            foreach ($skills as $skill) {
349
                $skill_select[$skill['id']] = $skill['name'];
350
            }
351
        }
352
353
        return $skill_select;
354
    }
355
356
    /**
357
     * Set the generate_certificates value.
358
     *
359
     * @param int $generateCertificates
360
     */
361
    public function setGenerateCertificates($generateCertificates)
362
    {
363
        $this->generateCertificates = $generateCertificates;
364
    }
365
366
    /**
367
     * Get the generate_certificates value.
368
     *
369
     * @return int
370
     */
371
    public function getGenerateCertificates()
372
    {
373
        return $this->generateCertificates;
374
    }
375
376
    /**
377
     * @param int $id
378
     * @param int $session_id
379
     *
380
     * @return array
381
     */
382
    public static function loadSessionCategories(
383
        $id = null,
384
        $session_id = null
385
    ) {
386
        if (isset($id) && (int) $id === 0) {
387
            $cats = [];
388
            $cats[] = self::create_root_category();
389
390
            return $cats;
391
        }
392
393
        $courseInfo = api_get_course_info_by_id(api_get_course_int_id());
394
        $courseCode = $courseInfo['code'];
395
        $session_id = (int) $session_id;
396
397
        if (!empty($session_id)) {
398
            $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
399
            $sql = 'SELECT id, course_code
400
                    FROM '.$table.'
401
                    WHERE session_id = '.$session_id;
402
            $result_session = Database::query($sql);
403
            if (Database::num_rows($result_session) > 0) {
404
                $categoryList = [];
405
                while ($data_session = Database::fetch_array($result_session)) {
406
                    $parent_id = $data_session['id'];
407
                    if ($data_session['course_code'] == $courseCode) {
408
                        $categories = self::load($parent_id);
409
                        $categoryList = array_merge($categoryList, $categories);
410
                    }
411
                }
412
413
                return $categoryList;
414
            }
415
        }
416
    }
417
418
    /**
419
     * Retrieve categories and return them as an array of Category objects.
420
     *
421
     * @param int    $id          category id
422
     * @param int    $user_id     (category owner)
423
     * @param string $course_code
424
     * @param int    $parent_id   parent category
425
     * @param bool   $visible
426
     * @param int    $session_id  (in case we are in a session)
427
     * @param bool   $order_by    Whether to show all "session"
428
     *                            categories (true) or hide them (false) in case there is no session id
429
     *
430
     * @return array
431
     */
432
    public static function load(
433
        $id = null,
434
        $user_id = null,
435
        $course_code = null,
436
        $parent_id = null,
437
        $visible = null,
438
        $session_id = null,
439
        $order_by = null
440
    ) {
441
        //if the category given is explicitly 0 (not null), then create
442
        // a root category object (in memory)
443
        if (isset($id) && (int) $id === 0) {
444
            $cats = [];
445
            $cats[] = self::create_root_category();
446
447
            return $cats;
448
        }
449
450
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
451
        $sql = 'SELECT * FROM '.$table;
452
        $paramcount = 0;
453
        if (isset($id)) {
454
            $sql .= ' WHERE id = '.intval($id);
455
            $paramcount++;
456
        }
457
458
        if (isset($user_id)) {
459
            $user_id = intval($user_id);
460
            if ($paramcount != 0) {
461
                $sql .= ' AND';
462
            } else {
463
                $sql .= ' WHERE';
464
            }
465
            $sql .= ' user_id = '.intval($user_id);
466
            $paramcount++;
467
        }
468
469
        if (isset($course_code)) {
470
            if ($paramcount != 0) {
471
                $sql .= ' AND';
472
            } else {
473
                $sql .= ' WHERE';
474
            }
475
476
            if ($course_code == '0') {
477
                $sql .= ' course_code is null ';
478
            } else {
479
                $sql .= " course_code = '".Database::escape_string($course_code)."'";
480
            }
481
482
            /*if ($show_session_categories !== true) {
483
                // a query on the course should show all
484
                // the categories inside sessions for this course
485
                // otherwise a special parameter is given to ask explicitely
486
                $sql .= " AND (session_id IS NULL OR session_id = 0) ";
487
            } else {*/
488
            if (empty($session_id)) {
489
                $sql .= ' AND (session_id IS NULL OR session_id = 0) ';
490
            } else {
491
                $sql .= ' AND session_id = '.(int) $session_id.' ';
492
            }
493
            //}
494
            $paramcount++;
495
        }
496
497
        if (isset($parent_id)) {
498
            if (0 != $paramcount) {
499
                $sql .= ' AND ';
500
            } else {
501
                $sql .= ' WHERE ';
502
            }
503
            $sql .= ' parent_id = '.intval($parent_id);
504
            $paramcount++;
505
        }
506
507
        if (isset($visible)) {
508
            if (0 != $paramcount) {
509
                $sql .= ' AND';
510
            } else {
511
                $sql .= ' WHERE';
512
            }
513
            $sql .= ' visible = '.intval($visible);
514
        }
515
516
        if (!empty($order_by)) {
517
            if (!empty($order_by) && $order_by != '') {
518
                $sql .= ' '.$order_by;
519
            }
520
        }
521
522
        $result = Database::query($sql);
523
        $categories = [];
524
        if (Database::num_rows($result) > 0) {
525
            $categories = self::create_category_objects_from_sql_result($result);
526
        }
527
528
        return $categories;
529
    }
530
531
    /**
532
     * Create a category object from a GradebookCategory entity.
533
     *
534
     * @param GradebookCategory $gradebookCategory The entity
535
     *
536
     * @return \Category
537
     */
538
    public static function createCategoryObjectFromEntity(GradebookCategory $gradebookCategory)
539
    {
540
        $category = new Category();
541
        $category->set_id($gradebookCategory->getId());
542
        $category->set_name($gradebookCategory->getName());
543
        $category->set_description($gradebookCategory->getDescription());
544
        $category->set_user_id($gradebookCategory->getUserId());
545
        $category->set_course_code($gradebookCategory->getCourseCode());
546
        $category->set_parent_id($gradebookCategory->getParentId());
547
        $category->set_weight($gradebookCategory->getWeight());
548
        $category->set_visible($gradebookCategory->getVisible());
549
        $category->set_session_id($gradebookCategory->getSessionId());
550
        $category->set_certificate_min_score($gradebookCategory->getCertifMinScore());
551
        $category->set_grade_model_id($gradebookCategory->getGradeModelId());
552
        $category->set_locked($gradebookCategory->getLocked());
553
        $category->setGenerateCertificates($gradebookCategory->getGenerateCertificates());
554
        $category->setIsRequirement($gradebookCategory->getIsRequirement());
555
556
        return $category;
557
    }
558
559
    /**
560
     * Insert this category into the database.
561
     */
562
    public function add()
563
    {
564
        if (isset($this->name) && '-1' == $this->name) {
565
            return false;
566
        }
567
568
        if (isset($this->name) && isset($this->user_id)) {
569
            $em = Database::getManager();
570
571
            $category = new GradebookCategory();
572
            $category->setName($this->name);
573
            $category->setDescription($this->description);
574
            $category->setUserId($this->user_id);
575
            $category->setCourseCode($this->course_code);
576
            $category->setParentId($this->parent);
577
            $category->setWeight($this->weight);
578
            $category->setVisible($this->visible);
579
            $category->setCertifMinScore($this->certificate_min_score);
580
            $category->setSessionId($this->session_id);
581
            $category->setGenerateCertificates($this->generateCertificates);
582
            $category->setGradeModelId($this->grade_model_id);
583
            $category->setIsRequirement($this->isRequirement);
584
            $category->setLocked(false);
585
586
            $em->persist($category);
587
            $em->flush();
588
589
            $id = $category->getId();
590
            $this->set_id($id);
591
592
            if (!empty($id)) {
593
                $parent_id = $this->get_parent_id();
594
                $grade_model_id = $this->get_grade_model_id();
595
                if ($parent_id == 0) {
596
                    //do something
597
                    if (isset($grade_model_id) &&
598
                        !empty($grade_model_id) &&
599
                        $grade_model_id != '-1'
600
                    ) {
601
                        $obj = new GradeModel();
602
                        $components = $obj->get_components($grade_model_id);
603
                        $default_weight_setting = api_get_setting('gradebook_default_weight');
604
                        $default_weight = 100;
605
                        if (isset($default_weight_setting)) {
606
                            $default_weight = $default_weight_setting;
607
                        }
608
                        foreach ($components as $component) {
609
                            $gradebook = new Gradebook();
610
                            $params = [];
611
612
                            $params['name'] = $component['acronym'];
613
                            $params['description'] = $component['title'];
614
                            $params['user_id'] = api_get_user_id();
615
                            $params['parent_id'] = $id;
616
                            $params['weight'] = $component['percentage'] / 100 * $default_weight;
617
                            $params['session_id'] = api_get_session_id();
618
                            $params['course_code'] = $this->get_course_code();
619
620
                            $gradebook->save($params);
621
                        }
622
                    }
623
                }
624
            }
625
626
            $gradebook = new Gradebook();
627
            $gradebook->updateSkillsToGradeBook(
628
                $this->id,
629
                $this->get_skills(false)
630
            );
631
632
            return $id;
633
        }
634
    }
635
636
    /**
637
     * Update the properties of this category in the database.
638
     *
639
     * @todo fix me
640
     */
641
    public function save()
642
    {
643
        $em = Database::getManager();
644
645
        /** @var GradebookCategory $gradebookCategory */
646
        $gradebookCategory = $em
647
            ->getRepository('ChamiloCoreBundle:GradebookCategory')
648
            ->find($this->id);
649
650
        if (empty($gradebookCategory)) {
651
            return false;
652
        }
653
654
        $gradebookCategory->setName($this->name);
655
        $gradebookCategory->setDescription($this->description);
656
        $gradebookCategory->setUserId($this->user_id);
657
        $gradebookCategory->setCourseCode($this->course_code);
658
        $gradebookCategory->setParentId($this->parent);
659
        $gradebookCategory->setWeight($this->weight);
660
        $gradebookCategory->setVisible($this->visible);
661
        $gradebookCategory->setCertifMinScore($this->certificate_min_score);
662
        $gradebookCategory->setGenerateCertificates(
663
            $this->generateCertificates
664
        );
665
        $gradebookCategory->setGradeModelId($this->grade_model_id);
666
        $gradebookCategory->setIsRequirement($this->isRequirement);
667
668
        $em->merge($gradebookCategory);
669
        $em->flush();
670
671
        if (!empty($this->id)) {
672
            $parent_id = $this->get_parent_id();
673
            $grade_model_id = $this->get_grade_model_id();
674
            if ($parent_id == 0) {
675
                if (isset($grade_model_id) &&
676
                    !empty($grade_model_id) &&
677
                    $grade_model_id != '-1'
678
                ) {
679
                    $obj = new GradeModel();
680
                    $components = $obj->get_components($grade_model_id);
681
                    $default_weight_setting = api_get_setting('gradebook_default_weight');
682
                    $default_weight = 100;
683
                    if (isset($default_weight_setting)) {
684
                        $default_weight = $default_weight_setting;
685
                    }
686
                    $final_weight = $this->get_weight();
687
                    if (!empty($final_weight)) {
688
                        $default_weight = $this->get_weight();
689
                    }
690
                    foreach ($components as $component) {
691
                        $gradebook = new Gradebook();
692
                        $params = [];
693
                        $params['name'] = $component['acronym'];
694
                        $params['description'] = $component['title'];
695
                        $params['user_id'] = api_get_user_id();
696
                        $params['parent_id'] = $this->id;
697
                        $params['weight'] = $component['percentage'] / 100 * $default_weight;
698
                        $params['session_id'] = api_get_session_id();
699
                        $params['course_code'] = $this->get_course_code();
700
                        $gradebook->save($params);
701
                    }
702
                }
703
            }
704
        }
705
706
        $gradebook = new Gradebook();
707
        $gradebook->updateSkillsToGradeBook(
708
            $this->id,
709
            $this->get_skills(false),
710
            true
711
        );
712
    }
713
714
    /**
715
     * Update link weights see #5168.
716
     *
717
     * @param type $new_weight
718
     */
719
    public function updateChildrenWeight($new_weight)
720
    {
721
        $links = $this->get_links();
722
        $old_weight = $this->get_weight();
723
724
        if (!empty($links)) {
725
            foreach ($links as $link_item) {
726
                if (isset($link_item)) {
727
                    $new_item_weight = $new_weight * $link_item->get_weight() / $old_weight;
728
                    $link_item->set_weight($new_item_weight);
729
                    $link_item->save();
730
                }
731
            }
732
        }
733
    }
734
735
    /**
736
     * Delete this evaluation from the database.
737
     */
738
    public function delete()
739
    {
740
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
741
        $sql = 'DELETE FROM '.$table.' WHERE id = '.intval($this->id);
742
        Database::query($sql);
743
    }
744
745
    /**
746
     * Delete the gradebook categories from a course, including course sessions.
747
     *
748
     * @param string $courseCode
749
     */
750
    public static function deleteFromCourse($courseCode)
751
    {
752
        $em = Database::getManager();
753
        $categories = $em
754
            ->createQuery(
755
                'SELECT DISTINCT gc.sessionId
756
                FROM ChamiloCoreBundle:GradebookCategory gc WHERE gc.courseCode = :code'
757
            )
758
            ->setParameter('code', $courseCode)
759
            ->getResult();
760
761
        foreach ($categories as $category) {
762
            $cats = self::load(
763
                null,
764
                null,
765
                $courseCode,
766
                null,
767
                null,
768
                (int) $category['sessionId']
769
            );
770
771
            if (!empty($cats)) {
772
                /** @var self $cat */
773
                foreach ($cats as $cat) {
774
                    $cat->delete_all();
775
                }
776
            }
777
        }
778
    }
779
780
    /**
781
     * Show message resource delete.
782
     *
783
     * @param string $courseCode
784
     *
785
     * @return mixed
786
     */
787
    public function show_message_resource_delete($courseCode)
788
    {
789
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
790
        $sql = 'SELECT count(*) AS num
791
                FROM '.$table.'
792
                WHERE
793
                    course_code = "'.Database::escape_string($courseCode).'" AND
794
                    visible = 3';
795
        $res = Database::query($sql);
796
        $option = Database::fetch_array($res, 'ASSOC');
797
        if ($option['num'] >= 1) {
798
            return '&nbsp;&nbsp;<span class="resource-deleted">(&nbsp;'.get_lang('ResourceDeleted').'&nbsp;)</span>';
799
        }
800
801
        return false;
802
    }
803
804
    /**
805
     * Shows all information of an category.
806
     *
807
     * @param int $categoryId
808
     *
809
     * @return array
810
     */
811
    public function showAllCategoryInfo($categoryId)
812
    {
813
        $categoryId = (int) $categoryId;
814
        if (empty($categoryId)) {
815
            return [];
816
        }
817
818
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
819
        $sql = 'SELECT * FROM '.$table.'
820
                WHERE id = '.$categoryId;
821
        $result = Database::query($sql);
822
        $row = Database::fetch_array($result, 'ASSOC');
823
824
        return $row;
825
    }
826
827
    /**
828
     * Checks if the certificate is available for the given user in this category.
829
     *
830
     * @param int $user_id User ID
831
     *
832
     * @return bool True if conditions match, false if fails
833
     */
834
    public function is_certificate_available($user_id)
835
    {
836
        $score = $this->calc_score(
837
            $user_id,
838
            null,
839
            $this->course_code,
840
            $this->session_id
841
        );
842
843
        if (isset($score) && isset($score[0])) {
844
            // Get a percentage score to compare to minimum certificate score
845
            // $certification_score = $score[0] / $score[1] * 100;
846
            // Get real score not a percentage.
847
            $certification_score = $score[0];
848
            if ($certification_score >= $this->certificate_min_score) {
849
                return true;
850
            }
851
        }
852
853
        return false;
854
    }
855
856
    /**
857
     * Is this category a course ?
858
     * A category is a course if it has a course code and no parent category.
859
     */
860
    public function is_course()
861
    {
862
        return isset($this->course_code) && !empty($this->course_code)
863
            && (!isset($this->parent) || $this->parent == 0);
864
    }
865
866
    /**
867
     * Calculate the score of this category.
868
     *
869
     * @param int    $stud_id     student id (default: all students - then the average is returned)
870
     * @param        $type
871
     * @param string $course_code
872
     * @param int    $session_id
873
     *
874
     * @return array (score sum, weight sum)
875
     *               or null if no scores available
876
     */
877
    public function calc_score(
878
        $stud_id = null,
879
        $type = null,
880
        $course_code = '',
881
        $session_id = null
882
    ) {
883
        $key = 'category:'.$this->id.'student:'.(int) $stud_id.'type:'.$type.'course:'.$course_code.'session:'.(int) $session_id;
884
        $useCache = api_get_configuration_value('gradebook_use_apcu_cache');
885
        $cacheAvailable = api_get_configuration_value('apc') && $useCache;
886
887
        if ($cacheAvailable) {
888
            $cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
889
            if ($cacheDriver->contains($key)) {
890
                return $cacheDriver->fetch($key);
891
            }
892
        }
893
        // Classic
894
        if (!empty($stud_id) && '' == $type) {
895
            if (!empty($course_code)) {
896
                $cats = $this->get_subcategories(
897
                    $stud_id,
898
                    $course_code,
899
                    $session_id
900
                );
901
                $evals = $this->get_evaluations($stud_id, false, $course_code);
902
                $links = $this->get_links($stud_id, false, $course_code);
903
            } else {
904
                $cats = $this->get_subcategories($stud_id);
905
                $evals = $this->get_evaluations($stud_id);
906
                $links = $this->get_links($stud_id);
907
            }
908
909
            // Calculate score
910
            $count = 0;
911
            $ressum = 0;
912
            $weightsum = 0;
913
914
            if (!empty($cats)) {
915
                /** @var Category $cat */
916
                foreach ($cats as $cat) {
917
                    $cat->set_session_id($session_id);
918
                    $cat->set_course_code($course_code);
919
                    $cat->setStudentList($this->getStudentList());
920
                    $score = $cat->calc_score(
921
                        $stud_id,
922
                        null,
923
                        $course_code,
924
                        $session_id
925
                    );
926
927
                    $catweight = 0;
928
                    if (0 != $cat->get_weight()) {
929
                        $catweight = $cat->get_weight();
930
                        $weightsum += $catweight;
931
                    }
932
933
                    if (isset($score) && !empty($score[1]) && !empty($catweight)) {
934
                        $ressum += $score[0] / $score[1] * $catweight;
935
                    }
936
                }
937
            }
938
939
            if (!empty($evals)) {
940
                /** @var Evaluation $eval */
941
                foreach ($evals as $eval) {
942
                    $eval->setStudentList($this->getStudentList());
943
                    $evalres = $eval->calc_score($stud_id);
944
                    if (isset($evalres) && 0 != $eval->get_weight()) {
945
                        $evalweight = $eval->get_weight();
946
                        $weightsum += $evalweight;
947
                        if (!empty($evalres[1])) {
948
                            $ressum += $evalres[0] / $evalres[1] * $evalweight;
949
                        }
950
                    } else {
951
                        if (0 != $eval->get_weight()) {
952
                            $evalweight = $eval->get_weight();
953
                            $weightsum += $evalweight;
954
                        }
955
                    }
956
                }
957
            }
958
959
            if (!empty($links)) {
960
                /** @var EvalLink|ExerciseLink $link */
961
                foreach ($links as $link) {
962
                    $link->setStudentList($this->getStudentList());
963
964
                    if ($session_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $session_id 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...
965
                        $link->set_session_id($session_id);
966
                    }
967
968
                    $linkres = $link->calc_score($stud_id, null);
969
                    if (!empty($linkres) && 0 != $link->get_weight()) {
970
                        $linkweight = $link->get_weight();
971
                        $link_res_denom = 0 == $linkres[1] ? 1 : $linkres[1];
972
                        $weightsum += $linkweight;
973
                        $ressum += $linkres[0] / $link_res_denom * $linkweight;
974
                    } else {
975
                        // Adding if result does not exists
976
                        if (0 != $link->get_weight()) {
977
                            $linkweight = $link->get_weight();
978
                            $weightsum += $linkweight;
979
                        }
980
                    }
981
                }
982
            }
983
        } else {
984
            if (!empty($course_code)) {
985
                $cats = $this->get_subcategories(
986
                    null,
987
                    $course_code,
988
                    $session_id
989
                );
990
                $evals = $this->get_evaluations(null, false, $course_code);
991
                $links = $this->get_links(null, false, $course_code);
992
            } else {
993
                $cats = $this->get_subcategories(null);
994
                $evals = $this->get_evaluations(null);
995
                $links = $this->get_links(null);
996
            }
997
998
            // Calculate score
999
            $ressum = 0;
1000
            $weightsum = 0;
1001
            $bestResult = 0;
1002
            $totalScorePerStudent = [];
1003
1004
            if (!empty($cats)) {
1005
                /** @var Category $cat */
1006
                foreach ($cats as $cat) {
1007
                    $cat->setStudentList($this->getStudentList());
1008
                    $score = $cat->calc_score(
1009
                        null,
1010
                        $type,
1011
                        $course_code,
1012
                        $session_id
1013
                    );
1014
1015
                    $catweight = 0;
1016
                    if (0 != $cat->get_weight()) {
1017
                        $catweight = $cat->get_weight();
1018
                        $weightsum += $catweight;
1019
                    }
1020
1021
                    if (isset($score) && !empty($score[1]) && !empty($catweight)) {
1022
                        $ressum += $score[0] / $score[1] * $catweight;
1023
1024
                        if ($ressum > $bestResult) {
1025
                            $bestResult = $ressum;
1026
                        }
1027
                    }
1028
                }
1029
            }
1030
1031
            if (!empty($evals)) {
1032
                if ('best' === $type) {
1033
                    $studentList = $this->getStudentList();
1034
                    foreach ($studentList as $student) {
1035
                        $studentId = $student['user_id'];
1036
                        foreach ($evals as $eval) {
1037
                            $linkres = $eval->calc_score($studentId, null);
1038
                            $linkweight = $eval->get_weight();
1039
                            $link_res_denom = 0 == $linkres[1] ? 1 : $linkres[1];
1040
                            $ressum = $linkres[0] / $link_res_denom * $linkweight;
1041
1042
                            if (!isset($totalScorePerStudent[$studentId])) {
1043
                                $totalScorePerStudent[$studentId] = 0;
1044
                            }
1045
                            $totalScorePerStudent[$studentId] += $ressum;
1046
                        }
1047
                    }
1048
                } else {
1049
                    /** @var Evaluation $eval */
1050
                    foreach ($evals as $eval) {
1051
                        $evalres = $eval->calc_score(null, $type);
1052
                        $eval->setStudentList($this->getStudentList());
1053
1054
                        if (isset($evalres) && 0 != $eval->get_weight()) {
1055
                            $evalweight = $eval->get_weight();
1056
                            $weightsum += $evalweight;
1057
                            if (!empty($evalres[1])) {
1058
                                $ressum += $evalres[0] / $evalres[1] * $evalweight;
1059
                            }
1060
1061
                            if ($ressum > $bestResult) {
1062
                                $bestResult = $ressum;
1063
                            }
1064
                        } else {
1065
                            if (0 != $eval->get_weight()) {
1066
                                $evalweight = $eval->get_weight();
1067
                                $weightsum += $evalweight;
1068
                            }
1069
                        }
1070
                    }
1071
                }
1072
            }
1073
1074
            if (!empty($links)) {
1075
                $studentList = $this->getStudentList();
1076
                if ('best' === $type) {
1077
                    foreach ($studentList as $student) {
1078
                        $studentId = $student['user_id'];
1079
                        foreach ($links as $link) {
1080
                            $linkres = $link->calc_score($studentId, null);
1081
                            $linkweight = $link->get_weight();
1082
                            $link_res_denom = 0 == $linkres[1] ? 1 : $linkres[1];
1083
                            $ressum = $linkres[0] / $link_res_denom * $linkweight;
1084
1085
                            if (!isset($totalScorePerStudent[$studentId])) {
1086
                                $totalScorePerStudent[$studentId] = 0;
1087
                            }
1088
                            $totalScorePerStudent[$studentId] += $ressum;
1089
                        }
1090
                    }
1091
                } else {
1092
                    /** @var EvalLink|ExerciseLink $link */
1093
                    foreach ($links as $link) {
1094
                        $link->setStudentList($this->getStudentList());
1095
1096
                        if ($session_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $session_id 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...
1097
                            $link->set_session_id($session_id);
1098
                        }
1099
1100
                        $linkres = $link->calc_score($stud_id, $type);
1101
1102
                        if (!empty($linkres) && 0 != $link->get_weight()) {
1103
                            $linkweight = $link->get_weight();
1104
                            $link_res_denom = 0 == $linkres[1] ? 1 : $linkres[1];
1105
1106
                            $weightsum += $linkweight;
1107
                            $ressum += $linkres[0] / $link_res_denom * $linkweight;
1108
                            if ($ressum > $bestResult) {
1109
                                $bestResult = $ressum;
1110
                            }
1111
                        } else {
1112
                            // Adding if result does not exists
1113
                            if (0 != $link->get_weight()) {
1114
                                $linkweight = $link->get_weight();
1115
                                $weightsum += $linkweight;
1116
                            }
1117
                        }
1118
                    }
1119
                }
1120
            }
1121
        }
1122
1123
        switch ($type) {
1124
            case 'best':
1125
                arsort($totalScorePerStudent);
1126
                $maxScore = current($totalScorePerStudent);
1127
1128
                return [$maxScore, $this->get_weight()];
1129
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1130
            case 'average':
1131
                if (empty($ressum)) {
1132
                    if ($cacheAvailable) {
1133
                        $cacheDriver->save($key, null);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cacheDriver does not seem to be defined for all execution paths leading up to this point.
Loading history...
1134
                    }
1135
1136
                    return null;
1137
                }
1138
1139
                if ($cacheAvailable) {
1140
                    $cacheDriver->save($key, [$ressum, $weightsum]);
1141
                }
1142
1143
                return [$ressum, $weightsum];
1144
                break;
1145
            case 'ranking':
1146
                // category ranking is calculated in gradebook_data_generator.class.php
1147
                // function get_data
1148
                return null;
1149
1150
                return AbstractLink::getCurrentUserRanking($stud_id, []);
0 ignored issues
show
Unused Code introduced by
return AbstractLink::get...king($stud_id, array()) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1151
                break;
1152
            default:
1153
                if ($cacheAvailable) {
1154
                    $cacheDriver->save($key, [$ressum, $weightsum]);
1155
                }
1156
1157
                return [$ressum, $weightsum];
1158
                break;
1159
        }
1160
    }
1161
1162
    /**
1163
     * Delete this category and every subcategory, evaluation and result inside.
1164
     */
1165
    public function delete_all()
1166
    {
1167
        $cats = self::load(null, null, $this->course_code, $this->id, null);
1168
        $evals = Evaluation::load(
1169
            null,
1170
            null,
1171
            $this->course_code,
1172
            $this->id,
1173
            null
1174
        );
1175
1176
        $links = LinkFactory::load(
1177
            null,
1178
            null,
1179
            null,
1180
            null,
1181
            $this->course_code,
1182
            $this->id,
1183
            null
1184
        );
1185
1186
        if (!empty($cats)) {
1187
            /** @var Category $cat */
1188
            foreach ($cats as $cat) {
1189
                $cat->delete_all();
1190
                $cat->delete();
1191
            }
1192
        }
1193
1194
        if (!empty($evals)) {
1195
            /** @var Evaluation $eval */
1196
            foreach ($evals as $eval) {
1197
                $eval->delete_with_results();
1198
            }
1199
        }
1200
1201
        if (!empty($links)) {
1202
            /** @var AbstractLink $link */
1203
            foreach ($links as $link) {
1204
                $link->delete();
1205
            }
1206
        }
1207
1208
        $this->delete();
1209
    }
1210
1211
    /**
1212
     * Return array of Category objects where a student is subscribed to.
1213
     *
1214
     * @param int    $stud_id
1215
     * @param string $course_code
1216
     * @param int    $session_id
1217
     *
1218
     * @return array
1219
     */
1220
    public function get_root_categories_for_student(
1221
        $stud_id,
1222
        $course_code = null,
1223
        $session_id = null
1224
    ) {
1225
        $main_course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1226
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
1227
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1228
1229
        $course_code = Database::escape_string($course_code);
1230
        $session_id = (int) $session_id;
1231
1232
        $sql = "SELECT * FROM $table WHERE parent_id = 0";
1233
1234
        if (!api_is_allowed_to_edit()) {
1235
            $sql .= ' AND visible = 1';
1236
            //proceed with checks on optional parameters course & session
1237
            if (!empty($course_code)) {
1238
                // TODO: considering it highly improbable that a user would get here
1239
                // if he doesn't have the rights to view this course and this
1240
                // session, we don't check his registration to these, but this
1241
                // could be an improvement
1242
                if (!empty($session_id)) {
1243
                    $sql .= " AND course_code = '".$course_code."' AND session_id = ".$session_id;
1244
                } else {
1245
                    $sql .= " AND course_code = '".$course_code."' AND session_id is null OR session_id=0";
1246
                }
1247
            } else {
1248
                //no optional parameter, proceed as usual
1249
                $sql .= ' AND course_code in
1250
                     (
1251
                        SELECT c.code
1252
                        FROM '.$main_course_user_table.' cu INNER JOIN '.$courseTable.' c
1253
                        ON (cu.c_id = c.id)
1254
                        WHERE cu.user_id = '.intval($stud_id).'
1255
                        AND cu.status = '.STUDENT.'
1256
                    )';
1257
            }
1258
        } elseif (api_is_allowed_to_edit() && !api_is_platform_admin()) {
1259
            //proceed with checks on optional parameters course & session
1260
            if (!empty($course_code)) {
1261
                // TODO: considering it highly improbable that a user would get here
1262
                // if he doesn't have the rights to view this course and this
1263
                // session, we don't check his registration to these, but this
1264
                // could be an improvement
1265
                $sql .= " AND course_code  = '".$course_code."'";
1266
                if (!empty($session_id)) {
1267
                    $sql .= " AND session_id = ".$session_id;
1268
                } else {
1269
                    $sql .= 'AND session_id IS NULL OR session_id = 0';
1270
                }
1271
            } else {
1272
                $sql .= ' AND course_code IN
1273
                     (
1274
                        SELECT c.code
1275
                        FROM '.$main_course_user_table.' cu INNER JOIN '.$courseTable.' c
1276
                        ON (cu.c_id = c.id)
1277
                        WHERE
1278
                            cu.user_id = '.api_get_user_id().' AND
1279
                            cu.status = '.COURSEMANAGER.'
1280
                    )';
1281
            }
1282
        } elseif (api_is_platform_admin()) {
1283
            if (isset($session_id) && 0 != $session_id) {
1284
                $sql .= ' AND session_id='.$session_id;
1285
            } else {
1286
                $sql .= ' AND coalesce(session_id,0)=0';
1287
            }
1288
        }
1289
        $result = Database::query($sql);
1290
        $cats = self::create_category_objects_from_sql_result($result);
1291
1292
        // course independent categories
1293
        if (empty($course_code)) {
1294
            $cats = $this->getIndependentCategoriesWithStudentResult(
1295
                0,
1296
                $stud_id,
1297
                $cats
1298
            );
1299
        }
1300
1301
        return $cats;
1302
    }
1303
1304
    /**
1305
     * Return array of Category objects where a teacher is admin for.
1306
     *
1307
     * @param int    $user_id     (to return everything, use 'null' here)
1308
     * @param string $course_code (optional)
1309
     * @param int    $session_id  (optional)
1310
     *
1311
     * @return array
1312
     */
1313
    public function get_root_categories_for_teacher(
1314
        $user_id,
1315
        $course_code = null,
1316
        $session_id = null
1317
    ) {
1318
        if (null == $user_id) {
1319
            return self::load(null, null, $course_code, 0, null, $session_id);
1320
        }
1321
1322
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
1323
        $main_course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1324
        $tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1325
1326
        $sql = 'SELECT * FROM '.$tbl_grade_categories.'
1327
                WHERE parent_id = 0 ';
1328
        if (!empty($course_code)) {
1329
            $sql .= " AND course_code = '".Database::escape_string($course_code)."' ";
1330
            if (!empty($session_id)) {
1331
                $sql .= " AND session_id = ".(int) $session_id;
1332
            }
1333
        } else {
1334
            $sql .= ' AND course_code in
1335
                 (
1336
                    SELECT c.code
1337
                    FROM '.$main_course_user_table.' cu
1338
                    INNER JOIN '.$courseTable.' c
1339
                    ON (cu.c_id = c.id)
1340
                    WHERE user_id = '.intval($user_id).'
1341
                )';
1342
        }
1343
        $result = Database::query($sql);
1344
        $cats = self::create_category_objects_from_sql_result($result);
1345
        // course independent categories
1346
        if (isset($course_code)) {
1347
            $indcats = self::load(
1348
                null,
1349
                $user_id,
1350
                $course_code,
1351
                0,
1352
                null,
1353
                $session_id
1354
            );
1355
            $cats = array_merge($cats, $indcats);
1356
        }
1357
1358
        return $cats;
1359
    }
1360
1361
    /**
1362
     * Can this category be moved to somewhere else ?
1363
     * The root and courses cannot be moved.
1364
     *
1365
     * @return bool
1366
     */
1367
    public function is_movable()
1368
    {
1369
        return !(!isset($this->id) || 0 == $this->id || $this->is_course());
1370
    }
1371
1372
    /**
1373
     * Generate an array of possible categories where this category can be moved to.
1374
     * Notice: its own parent will be included in the list: it's up to the frontend
1375
     * to disable this element.
1376
     *
1377
     * @return array 2-dimensional array - every element contains 3 subelements (id, name, level)
1378
     */
1379
    public function get_target_categories()
1380
    {
1381
        // the root or a course -> not movable
1382
        if (!$this->is_movable()) {
1383
            return null;
1384
        } else {
1385
            // otherwise:
1386
            // - course independent category
1387
            //   -> movable to root or other independent categories
1388
            // - category inside a course
1389
            //   -> movable to root, independent categories or categories inside the course
1390
            $user = api_is_platform_admin() ? null : api_get_user_id();
1391
            $targets = [];
1392
            $level = 0;
1393
1394
            $root = [0, get_lang('RootCat'), $level];
1395
            $targets[] = $root;
1396
1397
            if (isset($this->course_code) && !empty($this->course_code)) {
1398
                $crscats = self::load(null, null, $this->course_code, 0);
1399
                foreach ($crscats as $cat) {
1400
                    if ($this->can_be_moved_to_cat($cat)) {
1401
                        $targets[] = [
1402
                            $cat->get_id(),
1403
                            $cat->get_name(),
1404
                            $level + 1,
1405
                        ];
1406
                        $targets = $this->addTargetSubcategories(
1407
                            $targets,
1408
                            $level + 1,
1409
                            $cat->get_id()
1410
                        );
1411
                    }
1412
                }
1413
            }
1414
1415
            $indcats = self::load(null, $user, 0, 0);
1416
            foreach ($indcats as $cat) {
1417
                if ($this->can_be_moved_to_cat($cat)) {
1418
                    $targets[] = [$cat->get_id(), $cat->get_name(), $level + 1];
1419
                    $targets = $this->addTargetSubcategories(
1420
                        $targets,
1421
                        $level + 1,
1422
                        $cat->get_id()
1423
                    );
1424
                }
1425
            }
1426
1427
            return $targets;
1428
        }
1429
    }
1430
1431
    /**
1432
     * Move this category to the given category.
1433
     * If this category moves from inside a course to outside,
1434
     * its course code must be changed, as well as the course code
1435
     * of all underlying categories and evaluations. All links will
1436
     * be deleted as well !
1437
     */
1438
    public function move_to_cat($cat)
1439
    {
1440
        $this->set_parent_id($cat->get_id());
1441
        if ($this->get_course_code() != $cat->get_course_code()) {
1442
            $this->set_course_code($cat->get_course_code());
1443
            $this->applyCourseCodeToChildren();
1444
        }
1445
        $this->save();
1446
    }
1447
1448
    /**
1449
     * Generate an array of all categories the user can navigate to.
1450
     */
1451
    public function get_tree()
1452
    {
1453
        $targets = [];
1454
        $level = 0;
1455
        $root = [0, get_lang('RootCat'), $level];
1456
        $targets[] = $root;
1457
1458
        // course or platform admin
1459
        if (api_is_allowed_to_edit()) {
1460
            $user = api_is_platform_admin() ? null : api_get_user_id();
1461
            $cats = self::get_root_categories_for_teacher($user);
0 ignored issues
show
Bug Best Practice introduced by
The method Category::get_root_categories_for_teacher() 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

1461
            /** @scrutinizer ignore-call */ 
1462
            $cats = self::get_root_categories_for_teacher($user);
Loading history...
1462
            foreach ($cats as $cat) {
1463
                $targets[] = [
1464
                    $cat->get_id(),
1465
                    $cat->get_name(),
1466
                    $level + 1,
1467
                ];
1468
                $targets = $this->add_subtree(
1469
                    $targets,
1470
                    $level + 1,
1471
                    $cat->get_id(),
1472
                    null
1473
                );
1474
            }
1475
        } else {
1476
            // student
1477
            $cats = $this->get_root_categories_for_student(api_get_user_id());
1478
            foreach ($cats as $cat) {
1479
                $targets[] = [
1480
                    $cat->get_id(),
1481
                    $cat->get_name(),
1482
                    $level + 1,
1483
                ];
1484
                $targets = $this->add_subtree(
1485
                    $targets,
1486
                    $level + 1,
1487
                    $cat->get_id(),
1488
                    1
1489
                );
1490
            }
1491
        }
1492
1493
        return $targets;
1494
    }
1495
1496
    /**
1497
     * Generate an array of courses that a teacher hasn't created a category for.
1498
     *
1499
     * @param int $user_id
1500
     *
1501
     * @return array 2-dimensional array - every element contains 2 subelements (code, title)
1502
     */
1503
    public function get_not_created_course_categories($user_id)
1504
    {
1505
        $tbl_main_courses = Database::get_main_table(TABLE_MAIN_COURSE);
1506
        $tbl_main_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1507
        $tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1508
1509
        $user_id = (int) $user_id;
1510
1511
        $sql = 'SELECT DISTINCT(code), title
1512
                FROM '.$tbl_main_courses.' cc, '.$tbl_main_course_user.' cu
1513
                WHERE
1514
                    cc.id = cu.c_id AND
1515
                    cu.status = '.COURSEMANAGER;
1516
1517
        if (!api_is_platform_admin()) {
1518
            $sql .= ' AND cu.user_id = '.$user_id;
1519
        }
1520
        $sql .= ' AND cc.code NOT IN
1521
             (
1522
                SELECT course_code FROM '.$tbl_grade_categories.'
1523
                WHERE
1524
                    parent_id = 0 AND
1525
                    course_code IS NOT NULL
1526
                )';
1527
        $result = Database::query($sql);
1528
1529
        $cats = [];
1530
        while ($data = Database::fetch_array($result)) {
1531
            $cats[] = [$data['code'], $data['title']];
1532
        }
1533
1534
        return $cats;
1535
    }
1536
1537
    /**
1538
     * Generate an array of all courses that a teacher is admin of.
1539
     *
1540
     * @param int $user_id
1541
     *
1542
     * @return array 2-dimensional array - every element contains 2 subelements (code, title)
1543
     */
1544
    public function get_all_courses($user_id)
1545
    {
1546
        $tbl_main_courses = Database::get_main_table(TABLE_MAIN_COURSE);
1547
        $tbl_main_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1548
        $sql = 'SELECT DISTINCT(code), title
1549
                FROM '.$tbl_main_courses.' cc, '.$tbl_main_course_user.' cu
1550
                WHERE cc.id = cu.c_id AND cu.status = '.COURSEMANAGER;
1551
        if (!api_is_platform_admin()) {
1552
            $sql .= ' AND cu.user_id = '.intval($user_id);
1553
        }
1554
1555
        $result = Database::query($sql);
1556
        $cats = [];
1557
        while ($data = Database::fetch_array($result)) {
1558
            $cats[] = [$data['code'], $data['title']];
1559
        }
1560
1561
        return $cats;
1562
    }
1563
1564
    /**
1565
     * Apply the same visibility to every subcategory, evaluation and link.
1566
     */
1567
    public function apply_visibility_to_children()
1568
    {
1569
        $cats = self::load(null, null, null, $this->id, null);
1570
        $evals = Evaluation::load(null, null, null, $this->id, null);
1571
        $links = LinkFactory::load(
1572
            null,
1573
            null,
1574
            null,
1575
            null,
1576
            null,
1577
            $this->id,
1578
            null
1579
        );
1580
        if (!empty($cats)) {
1581
            foreach ($cats as $cat) {
1582
                $cat->set_visible($this->is_visible());
1583
                $cat->save();
1584
                $cat->apply_visibility_to_children();
1585
            }
1586
        }
1587
        if (!empty($evals)) {
1588
            foreach ($evals as $eval) {
1589
                $eval->set_visible($this->is_visible());
1590
                $eval->save();
1591
            }
1592
        }
1593
        if (!empty($links)) {
1594
            foreach ($links as $link) {
1595
                $link->set_visible($this->is_visible());
1596
                $link->save();
1597
            }
1598
        }
1599
    }
1600
1601
    /**
1602
     * Check if a category contains evaluations with a result for a given student.
1603
     *
1604
     * @param int $studentId
1605
     *
1606
     * @return bool
1607
     */
1608
    public function hasEvaluationsWithStudentResults($studentId)
1609
    {
1610
        $evals = Evaluation::get_evaluations_with_result_for_student(
1611
            $this->id,
1612
            $studentId
1613
        );
1614
        if (0 != count($evals)) {
1615
            return true;
1616
        } else {
1617
            $cats = self::load(
1618
                null,
1619
                null,
1620
                null,
1621
                $this->id,
1622
                api_is_allowed_to_edit() ? null : 1
1623
            );
1624
1625
            /** @var Category $cat */
1626
            foreach ($cats as $cat) {
1627
                if ($cat->hasEvaluationsWithStudentResults($studentId)) {
1628
                    return true;
1629
                }
1630
            }
1631
1632
            return false;
1633
        }
1634
    }
1635
1636
    /**
1637
     * Retrieve all categories inside a course independent category
1638
     * that should be visible to a student.
1639
     *
1640
     * @param int   $categoryId parent category
1641
     * @param int   $studentId
1642
     * @param array $cats       optional: if defined, the categories will be added to this array
1643
     *
1644
     * @return array
1645
     */
1646
    public function getIndependentCategoriesWithStudentResult(
1647
        $categoryId,
1648
        $studentId,
1649
        $cats = []
1650
    ) {
1651
        $creator = api_is_allowed_to_edit() && !api_is_platform_admin() ? api_get_user_id() : null;
1652
1653
        $categories = self::load(
1654
            null,
1655
            $creator,
1656
            '0',
1657
            $categoryId,
1658
            api_is_allowed_to_edit() ? null : 1
1659
        );
1660
1661
        if (!empty($categories)) {
1662
            /** @var Category $category */
1663
            foreach ($categories as $category) {
1664
                if ($category->hasEvaluationsWithStudentResults($studentId)) {
1665
                    $cats[] = $category;
1666
                }
1667
            }
1668
        }
1669
1670
        return $cats;
1671
    }
1672
1673
    /**
1674
     * Return the session id (in any case, even if it's null or 0).
1675
     *
1676
     * @return int Session id (can be null)
1677
     */
1678
    public function get_session_id()
1679
    {
1680
        return $this->session_id;
1681
    }
1682
1683
    /**
1684
     * Get appropriate subcategories visible for the user (and optionally the course and session).
1685
     *
1686
     * @param int    $studentId   student id (default: all students)
1687
     * @param string $course_code Course code (optional)
1688
     * @param int    $session_id  Session ID (optional)
1689
     * @param bool   $order
1690
     *
1691
     * @return array Array of subcategories
1692
     */
1693
    public function get_subcategories(
1694
        $studentId = null,
1695
        $course_code = null,
1696
        $session_id = null,
1697
        $order = null
1698
    ) {
1699
        // 1 student
1700
        if (isset($studentId)) {
1701
            // Special case: this is the root
1702
            if (0 == $this->id) {
1703
                return $this->get_root_categories_for_student($studentId, $course_code, $session_id);
1704
            } else {
1705
                return self::load(
1706
                    null,
1707
                    null,
1708
                    $course_code,
1709
                    $this->id,
1710
                    api_is_allowed_to_edit() ? null : 1,
1711
                    $session_id,
1712
                    $order
1713
                );
1714
            }
1715
        } else {
1716
            // All students
1717
            // Course admin
1718
            if (api_is_allowed_to_edit() && !api_is_platform_admin()) {
1719
                // root
1720
                if (0 == $this->id) {
1721
                    // inside a course
1722
                    return $this->get_root_categories_for_teacher(
1723
                        api_get_user_id(),
1724
                        $course_code,
1725
                        $session_id,
1726
                        false
1727
                    );
1728
                } elseif (!empty($this->course_code)) {
1729
                    return self::load(
1730
                        null,
1731
                        null,
1732
                        $this->course_code,
1733
                        $this->id,
1734
                        null,
1735
                        $session_id,
1736
                        $order
1737
                    );
1738
                } elseif (!empty($course_code)) {
1739
                    // course independent
1740
                    return self::load(
1741
                        null,
1742
                        null,
1743
                        $course_code,
1744
                        $this->id,
1745
                        null,
1746
                        $session_id,
1747
                        $order
1748
                    );
1749
                } else {
1750
                    return self::load(
1751
                        null,
1752
                        api_get_user_id(),
1753
                        0,
1754
                        $this->id,
1755
                        null
1756
                    );
1757
                }
1758
            } elseif (api_is_platform_admin()) {
1759
                // platform admin
1760
                // we explicitly avoid listing subcats from another session
1761
                return self::load(
1762
                    null,
1763
                    null,
1764
                    $course_code,
1765
                    $this->id,
1766
                    null,
1767
                    $session_id,
1768
                    $order
1769
                );
1770
            }
1771
        }
1772
1773
        return [];
1774
    }
1775
1776
    /**
1777
     * Get appropriate evaluations visible for the user.
1778
     *
1779
     * @param int    $studentId   student id (default: all students)
1780
     * @param bool   $recursive   process subcategories (default: no recursion)
1781
     * @param string $course_code
1782
     * @param int    $sessionId
1783
     *
1784
     * @return array
1785
     */
1786
    public function get_evaluations(
1787
        $studentId = null,
1788
        $recursive = false,
1789
        $course_code = '',
1790
        $sessionId = 0
1791
    ) {
1792
        $evals = [];
1793
        $course_code = empty($course_code) ? $this->get_course_code() : $course_code;
1794
        $sessionId = empty($sessionId) ? $this->get_session_id() : $sessionId;
1795
1796
        // 1 student
1797
        if (isset($studentId) && !empty($studentId)) {
1798
            // Special case: this is the root
1799
            if (0 == $this->id) {
1800
                $evals = Evaluation::get_evaluations_with_result_for_student(
1801
                    0,
1802
                    $studentId
1803
                );
1804
            } else {
1805
                $evals = Evaluation::load(
1806
                    null,
1807
                    null,
1808
                    $course_code,
1809
                    $this->id,
1810
                    api_is_allowed_to_edit() ? null : 1
1811
                );
1812
            }
1813
        } else {
1814
            // All students
1815
            // course admin
1816
            if ((api_is_allowed_to_edit() || api_is_drh() || api_is_session_admin()) &&
1817
                !api_is_platform_admin()
1818
            ) {
1819
                // root
1820
                if (0 == $this->id) {
1821
                    $evals = Evaluation::load(
1822
                        null,
1823
                        api_get_user_id(),
1824
                        null,
1825
                        $this->id,
1826
                        null
1827
                    );
1828
                } elseif (isset($this->course_code) &&
1829
                    !empty($this->course_code)
1830
                ) {
1831
                    // inside a course
1832
                    $evals = Evaluation::load(
1833
                        null,
1834
                        null,
1835
                        $course_code,
1836
                        $this->id,
1837
                        null
1838
                    );
1839
                } else {
1840
                    // course independent
1841
                    $evals = Evaluation::load(
1842
                        null,
1843
                        api_get_user_id(),
1844
                        null,
1845
                        $this->id,
1846
                        null
1847
                    );
1848
                }
1849
            } else {
1850
                $evals = Evaluation::load(
1851
                    null,
1852
                    null,
1853
                    $course_code,
1854
                    $this->id,
1855
                    null
1856
                );
1857
            }
1858
        }
1859
1860
        if ($recursive) {
1861
            $subcats = $this->get_subcategories(
1862
                $studentId,
1863
                $course_code,
1864
                $sessionId
1865
            );
1866
1867
            if (!empty($subcats)) {
1868
                foreach ($subcats as $subcat) {
1869
                    $subevals = $subcat->get_evaluations(
1870
                        $studentId,
1871
                        true,
1872
                        $course_code
1873
                    );
1874
                    $evals = array_merge($evals, $subevals);
1875
                }
1876
            }
1877
        }
1878
1879
        return $evals;
1880
    }
1881
1882
    /**
1883
     * Get appropriate links visible for the user.
1884
     *
1885
     * @param int    $studentId   student id (default: all students)
1886
     * @param bool   $recursive   process subcategories (default: no recursion)
1887
     * @param string $course_code
1888
     * @param int    $sessionId
1889
     *
1890
     * @return array
1891
     */
1892
    public function get_links(
1893
        $studentId = null,
1894
        $recursive = false,
1895
        $course_code = '',
1896
        $sessionId = 0
1897
    ) {
1898
        $links = [];
1899
        $course_code = empty($course_code) ? $this->get_course_code() : $course_code;
1900
        $sessionId = empty($sessionId) ? $this->get_session_id() : $sessionId;
1901
1902
        // no links in root or course independent categories
1903
        if (0 == $this->id) {
1904
        } elseif (isset($studentId)) {
1905
            // 1 student $studentId
1906
            $links = LinkFactory::load(
1907
                null,
1908
                null,
1909
                null,
1910
                null,
1911
                $course_code,
1912
                $this->id,
1913
                api_is_allowed_to_edit() ? null : 1
1914
            );
1915
        } else {
1916
            // All students -> only for course/platform admin
1917
            $links = LinkFactory::load(
1918
                null,
1919
                null,
1920
                null,
1921
                null,
1922
                $course_code,
1923
                $this->id,
1924
                null
1925
            );
1926
        }
1927
1928
        if ($recursive) {
1929
            $subcats = $this->get_subcategories(
1930
                $studentId,
1931
                $course_code,
1932
                $sessionId
1933
            );
1934
            if (!empty($subcats)) {
1935
                /** @var Category $subcat */
1936
                foreach ($subcats as $subcat) {
1937
                    $sublinks = $subcat->get_links(
1938
                        $studentId,
1939
                        false,
1940
                        $course_code,
1941
                        $sessionId
1942
                    );
1943
                    $links = array_merge($links, $sublinks);
1944
                }
1945
            }
1946
        }
1947
1948
        return $links;
1949
    }
1950
1951
    /**
1952
     * Get all the categories from with the same given direct parent.
1953
     *
1954
     * @param int $catId Category parent ID
1955
     *
1956
     * @return array Array of Category objects
1957
     */
1958
    public function getCategories($catId)
1959
    {
1960
        $catId = (int) $catId;
1961
        $tblGradeCategories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1962
        $sql = 'SELECT * FROM '.$tblGradeCategories.'
1963
                WHERE parent_id = '.$catId;
1964
1965
        $result = Database::query($sql);
1966
        $categories = self::create_category_objects_from_sql_result($result);
1967
1968
        return $categories;
1969
    }
1970
1971
    /**
1972
     * Gets the type for the current object.
1973
     *
1974
     * @return string 'C' to represent "Category" object type
1975
     */
1976
    public function get_item_type()
1977
    {
1978
        return 'C';
1979
    }
1980
1981
    /**
1982
     * @param array $skills
1983
     */
1984
    public function set_skills($skills)
1985
    {
1986
        $this->skills = $skills;
1987
    }
1988
1989
    public function get_date()
1990
    {
1991
        return null;
1992
    }
1993
1994
    /**
1995
     * @return string
1996
     */
1997
    public function get_icon_name()
1998
    {
1999
        return 'cat';
2000
    }
2001
2002
    /**
2003
     * Find category by name.
2004
     *
2005
     * @param string $name_mask search string
2006
     *
2007
     * @return array category objects matching the search criterium
2008
     */
2009
    public function find_category($name_mask, $allcat)
2010
    {
2011
        $categories = [];
2012
        foreach ($allcat as $search_cat) {
2013
            if (!(strpos(strtolower($search_cat->get_name()), strtolower($name_mask)) === false)) {
2014
                $categories[] = $search_cat;
2015
            }
2016
        }
2017
2018
        return $categories;
2019
    }
2020
2021
    /**
2022
     * This function, locks a category , only one who can unlock it is
2023
     * the platform administrator.
2024
     *
2025
     * @param int locked 1 or unlocked 0
2026
2027
     *
2028
     * @return bool|null
2029
     * */
2030
    public function lock($locked)
2031
    {
2032
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
2033
        $sql = "UPDATE $table SET locked = '".intval($locked)."'
2034
                WHERE id='".intval($this->id)."'";
2035
        Database::query($sql);
2036
    }
2037
2038
    /**
2039
     * @param $locked
2040
     */
2041
    public function lockAllItems($locked)
2042
    {
2043
        if ('true' == api_get_setting('gradebook_locking_enabled')) {
2044
            $this->lock($locked);
2045
            $evals_to_lock = $this->get_evaluations();
2046
            if (!empty($evals_to_lock)) {
2047
                foreach ($evals_to_lock as $item) {
2048
                    $item->lock($locked);
2049
                }
2050
            }
2051
2052
            $link_to_lock = $this->get_links();
2053
            if (!empty($link_to_lock)) {
2054
                foreach ($link_to_lock as $item) {
2055
                    $item->lock($locked);
2056
                }
2057
            }
2058
2059
            $event_type = LOG_GRADEBOOK_UNLOCKED;
2060
            if (1 == $locked) {
2061
                $event_type = LOG_GRADEBOOK_LOCKED;
2062
            }
2063
            Event::addEvent($event_type, LOG_GRADEBOOK_ID, $this->id);
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

2063
            Event::/** @scrutinizer ignore-call */ 
2064
                   addEvent($event_type, LOG_GRADEBOOK_ID, $this->id);

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...
2064
        }
2065
    }
2066
2067
    /**
2068
     * Generates a certificate for this user if everything matches.
2069
     *
2070
     * @param int  $category_id      gradebook id
2071
     * @param int  $user_id
2072
     * @param bool $sendNotification
2073
     * @param bool $skipGenerationIfExists
2074
     *
2075
     * @return array
2076
     */
2077
    public static function generateUserCertificate(
2078
        $category_id,
2079
        $user_id,
2080
        $sendNotification = false,
2081
        $skipGenerationIfExists = false
2082
    ) {
2083
        $user_id = (int) $user_id;
2084
        $category_id = (int) $category_id;
2085
2086
        // Generating the total score for a course
2087
        $category = self::load(
2088
            $category_id,
2089
            null,
2090
            null,
2091
            null,
2092
            null,
2093
            null,
2094
            false
2095
        );
2096
2097
        /** @var Category $category */
2098
        $category = $category[0];
2099
2100
        if (empty($category)) {
2101
            return false;
2102
        }
2103
2104
        $sessionId = $category->get_session_id();
2105
        $courseCode = $category->get_course_code();
2106
        $courseInfo = api_get_course_info($courseCode);
2107
        $courseId = $courseInfo['real_id'];
2108
2109
        $userFinishedCourse = self::userFinishedCourse(
2110
            $user_id,
2111
            $category,
2112
            true
2113
        );
2114
2115
        if (!$userFinishedCourse) {
2116
            return false;
2117
        }
2118
2119
        $skillToolEnabled = Skill::hasAccessToUserSkill(
2120
            api_get_user_id(),
2121
            $user_id
2122
        );
2123
2124
        $userHasSkills = false;
2125
        if ($skillToolEnabled) {
2126
            $skill = new Skill();
2127
            $skill->addSkillToUser(
2128
                $user_id,
2129
                $category,
2130
                $courseId,
2131
                $sessionId
2132
            );
2133
2134
            $objSkillRelUser = new SkillRelUser();
2135
            $userSkills = $objSkillRelUser->getUserSkills(
2136
                $user_id,
2137
                $courseId,
2138
                $sessionId
2139
            );
2140
            $userHasSkills = !empty($userSkills);
2141
        }
2142
2143
        // Block certification links depending gradebook configuration (generate certifications)
2144
        if (empty($category->getGenerateCertificates())) {
2145
            if ($userHasSkills) {
2146
                return [
2147
                    'badge_link' => Display::toolbarButton(
2148
                        get_lang('ExportBadges'),
2149
                        api_get_path(WEB_CODE_PATH)."gradebook/get_badges.php?user=$user_id",
2150
                        'external-link'
2151
                    ),
2152
                ];
2153
            }
2154
2155
            return false;
2156
        }
2157
2158
        $scoretotal = $category->calc_score($user_id);
2159
2160
        // Do not remove this the gradebook/lib/fe/gradebooktable.class.php
2161
        // file load this variable as a global
2162
        $scoredisplay = ScoreDisplay::instance();
2163
        $my_score_in_gradebook = $scoredisplay->display_score(
2164
            $scoretotal,
2165
            SCORE_SIMPLE
2166
        );
2167
2168
        $my_certificate = GradebookUtils::get_certificate_by_user_id(
2169
            $category_id,
2170
            $user_id
2171
        );
2172
2173
        if ($skipGenerationIfExists && !empty($my_certificate)) {
2174
            return false;
2175
        }
2176
2177
        if (empty($my_certificate)) {
2178
            GradebookUtils::registerUserInfoAboutCertificate(
2179
                $category_id,
2180
                $user_id,
2181
                $my_score_in_gradebook,
2182
                api_get_utc_datetime()
2183
            );
2184
            $my_certificate = GradebookUtils::get_certificate_by_user_id(
2185
                $category_id,
2186
                $user_id
2187
            );
2188
        }
2189
2190
        $html = [];
2191
        if (!empty($my_certificate)) {
2192
            $certificate_obj = new Certificate(
2193
                $my_certificate['id'],
2194
                0,
2195
                $sendNotification
2196
            );
2197
2198
            $fileWasGenerated = $certificate_obj->isHtmlFileGenerated();
2199
2200
            // Fix when using custom certificate BT#15937
2201
            if (api_get_plugin_setting('customcertificate', 'enable_plugin_customcertificate') === 'true') {
2202
                $infoCertificate = CustomCertificatePlugin::getCertificateData($my_certificate['id'], $user_id);
2203
                if (!empty($infoCertificate)) {
2204
                    $fileWasGenerated = true;
2205
                }
2206
            }
2207
2208
            if (!empty($fileWasGenerated)) {
2209
                $url = api_get_path(WEB_PATH).'certificates/index.php?id='.$my_certificate['id'].'&user_id='.$user_id;
2210
                $certificates = Display::toolbarButton(
2211
                    get_lang('DisplayCertificate'),
2212
                    $url,
2213
                    'eye',
2214
                    'primary',
2215
                    ['target' => '_blank']
2216
                );
2217
2218
                $exportToPDF = Display::url(
2219
                    Display::return_icon(
2220
                        'pdf.png',
2221
                        get_lang('ExportToPDF'),
2222
                        [],
2223
                        ICON_SIZE_MEDIUM
2224
                    ),
2225
                    "$url&action=export"
2226
                );
2227
2228
                $hideExportLink = api_get_setting('hide_certificate_export_link');
2229
                $hideExportLinkStudent = api_get_setting('hide_certificate_export_link_students');
2230
                if ($hideExportLink === 'true' || (api_is_student() && $hideExportLinkStudent === 'true')) {
2231
                    $exportToPDF = null;
2232
                }
2233
2234
                $html = [
2235
                    'certificate_link' => $certificates,
2236
                    'pdf_link' => $exportToPDF,
2237
                    'pdf_url' => "$url&action=export",
2238
                ];
2239
            }
2240
2241
            if ($skillToolEnabled && $userHasSkills) {
2242
                $html['badge_link'] = Display::toolbarButton(
2243
                    get_lang('ExportBadges'),
2244
                    api_get_path(WEB_CODE_PATH)."gradebook/get_badges.php?user=$user_id",
2245
                    'external-link'
2246
                );
2247
            }
2248
2249
            return $html;
2250
        }
2251
    }
2252
2253
    /**
2254
     * @param int   $catId
2255
     * @param array $userList
2256
     */
2257
    public static function generateCertificatesInUserList($catId, $userList)
2258
    {
2259
        if (!empty($userList)) {
2260
            foreach ($userList as $userInfo) {
2261
                self::generateUserCertificate($catId, $userInfo['user_id']);
2262
            }
2263
        }
2264
    }
2265
2266
    /**
2267
     * @param int   $catId
2268
     * @param array $userList
2269
     */
2270
    public static function exportAllCertificates(
2271
        $catId,
2272
        $userList = []
2273
    ) {
2274
        $orientation = api_get_configuration_value('certificate_pdf_orientation');
2275
2276
        $params['orientation'] = 'landscape';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
2277
        if (!empty($orientation)) {
2278
            $params['orientation'] = $orientation;
2279
        }
2280
2281
        $params['left'] = 0;
2282
        $params['right'] = 0;
2283
        $params['top'] = 0;
2284
        $params['bottom'] = 0;
2285
        $page_format = $params['orientation'] === 'landscape' ? 'A4-L' : 'A4';
2286
        $pdf = new PDF($page_format, $params['orientation'], $params);
2287
2288
        $certificate_list = GradebookUtils::get_list_users_certificates($catId, $userList);
2289
        $certificate_path_list = [];
2290
2291
        if (!empty($certificate_list)) {
2292
            foreach ($certificate_list as $index => $value) {
2293
                $list_certificate = GradebookUtils::get_list_gradebook_certificates_by_user_id(
2294
                    $value['user_id'],
2295
                    $catId
2296
                );
2297
                foreach ($list_certificate as $value_certificate) {
2298
                    $certificate_obj = new Certificate($value_certificate['id']);
2299
                    $certificate_obj->generate(['hide_print_button' => true]);
2300
                    if ($certificate_obj->isHtmlFileGenerated()) {
2301
                        $certificate_path_list[] = $certificate_obj->html_file;
2302
                    }
2303
                }
2304
            }
2305
        }
2306
2307
        if (!empty($certificate_path_list)) {
2308
            // Print certificates (without the common header/footer/watermark
2309
            //  stuff) and return as one multiple-pages PDF
2310
            $pdf->html_to_pdf(
2311
                $certificate_path_list,
2312
                get_lang('Certificates'),
2313
                null,
2314
                false,
2315
                false
2316
            );
2317
        }
2318
    }
2319
2320
    /**
2321
     * @param int $catId
2322
     */
2323
    public static function deleteAllCertificates($catId)
2324
    {
2325
        $certificate_list = GradebookUtils::get_list_users_certificates($catId);
2326
        if (!empty($certificate_list)) {
2327
            foreach ($certificate_list as $index => $value) {
2328
                $list_certificate = GradebookUtils::get_list_gradebook_certificates_by_user_id(
2329
                    $value['user_id'],
2330
                    $catId
2331
                );
2332
                foreach ($list_certificate as $value_certificate) {
2333
                    $certificate_obj = new Certificate($value_certificate['id']);
2334
                    $certificate_obj->delete(true);
2335
                }
2336
            }
2337
        }
2338
    }
2339
2340
    /**
2341
     * Check whether a user has finished a course by its gradebook.
2342
     *
2343
     * @param int       $userId           The user ID
2344
     * @param \Category $category         Optional. The gradebook category.
2345
     *                                    To check by the gradebook category
2346
     * @param bool      $recalculateScore Whether recalculate the score
2347
     *
2348
     * @return bool
2349
     */
2350
    public static function userFinishedCourse(
2351
        $userId,
2352
        \Category $category,
2353
        $recalculateScore = false
2354
    ) {
2355
        if (empty($category)) {
2356
            return false;
2357
        }
2358
2359
        $currentScore = self::getCurrentScore(
2360
            $userId,
2361
            $category,
2362
            $recalculateScore
2363
        );
2364
2365
        $minCertificateScore = $category->getCertificateMinScore();
2366
2367
        return $currentScore >= $minCertificateScore;
2368
    }
2369
2370
    /**
2371
     * Get the current score (as percentage) on a gradebook category for a user.
2372
     *
2373
     * @param int      $userId      The user id
2374
     * @param Category $category    The gradebook category
2375
     * @param bool     $recalculate
2376
     *
2377
     * @return float The score
2378
     */
2379
    public static function getCurrentScore(
2380
        $userId,
2381
        $category,
2382
        $recalculate = false
2383
    ) {
2384
        if (empty($category)) {
2385
            return 0;
2386
        }
2387
2388
        if ($recalculate) {
2389
            return self::calculateCurrentScore(
2390
                $userId,
2391
                $category
2392
            );
2393
        }
2394
2395
        $resultData = Database::select(
2396
            '*',
2397
            Database::get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_LOG),
2398
            [
2399
                'where' => [
2400
                    'category_id = ? AND user_id = ?' => [$category->get_id(), $userId],
2401
                ],
2402
                'order' => 'registered_at DESC',
2403
                'limit' => '1',
2404
            ],
2405
            'first'
2406
        );
2407
2408
        if (empty($resultData)) {
2409
            return 0;
2410
        }
2411
2412
        return $resultData['score'];
2413
    }
2414
2415
    /**
2416
     * Register the current score for a user on a category gradebook.
2417
     *
2418
     * @param float $score      The achieved score
2419
     * @param int   $userId     The user id
2420
     * @param int   $categoryId The gradebook category
2421
     *
2422
     * @return int The insert id
2423
     */
2424
    public static function registerCurrentScore($score, $userId, $categoryId)
2425
    {
2426
        return Database::insert(
0 ignored issues
show
Bug Best Practice introduced by
The expression return Database::insert(...pi_get_utc_datetime())) could also return false which is incompatible with the documented return type integer. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
2427
            Database::get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_LOG),
2428
            [
2429
                'category_id' => intval($categoryId),
2430
                'user_id' => intval($userId),
2431
                'score' => api_float_val($score),
2432
                'registered_at' => api_get_utc_datetime(),
2433
            ]
2434
        );
2435
    }
2436
2437
    /**
2438
     * @return array
2439
     */
2440
    public function getStudentList()
2441
    {
2442
        return $this->studentList;
2443
    }
2444
2445
    /**
2446
     * @param array $list
2447
     */
2448
    public function setStudentList($list)
2449
    {
2450
        $this->studentList = $list;
2451
    }
2452
2453
    /**
2454
     * @return string
2455
     */
2456
    public static function getUrl()
2457
    {
2458
        $url = Session::read('gradebook_dest');
2459
        if (empty($url)) {
2460
            // We guess the link
2461
            $courseInfo = api_get_course_info();
2462
            if (!empty($courseInfo)) {
2463
                return api_get_path(WEB_CODE_PATH).'gradebook/index.php?'.api_get_cidreq().'&';
2464
            } else {
2465
                return api_get_path(WEB_CODE_PATH).'gradebook/gradebook.php?';
2466
            }
2467
        }
2468
2469
        return $url;
2470
    }
2471
2472
    /**
2473
     * Destination is index.php or gradebook.php.
2474
     *
2475
     * @param string $url
2476
     */
2477
    public static function setUrl($url)
2478
    {
2479
        switch ($url) {
2480
            case 'gradebook.php':
2481
                $url = api_get_path(WEB_CODE_PATH).'gradebook/gradebook.php?';
2482
                break;
2483
            case 'index.php':
2484
                $url = api_get_path(WEB_CODE_PATH).'gradebook/index.php?'.api_get_cidreq().'&';
2485
                break;
2486
        }
2487
        Session::write('gradebook_dest', $url);
2488
    }
2489
2490
    /**
2491
     * @return int
2492
     */
2493
    public function getGradeBooksToValidateInDependence()
2494
    {
2495
        return $this->gradeBooksToValidateInDependence;
2496
    }
2497
2498
    /**
2499
     * @param int $value
2500
     *
2501
     * @return Category
2502
     */
2503
    public function setGradeBooksToValidateInDependence($value)
2504
    {
2505
        $this->gradeBooksToValidateInDependence = $value;
2506
2507
        return $this;
2508
    }
2509
2510
    /**
2511
     * Return HTML code with links to download and view certificate.
2512
     *
2513
     * @return string
2514
     */
2515
    public static function getDownloadCertificateBlock(array $certificate)
2516
    {
2517
        if (!isset($certificate['pdf_url'])) {
2518
            return '';
2519
        }
2520
2521
        $downloadLink = Display::toolbarButton(
2522
            get_lang('DownloadCertificatePdf'),
2523
            $certificate['pdf_url'],
2524
            'file-pdf-o'
2525
        );
2526
        $viewLink = $certificate['certificate_link'];
2527
2528
        return "
2529
            <div class='panel panel-default'>
2530
                <div class='panel-body'>
2531
                    <h3 class='text-center'>".get_lang('NowDownloadYourCertificateClickHere')."</h3>
2532
                    <div class='text-center'>$downloadLink $viewLink</div>
2533
                </div>
2534
            </div>
2535
        ";
2536
    }
2537
2538
    /**
2539
     * Find a gradebook category by the certificate ID.
2540
     *
2541
     * @param int $id certificate id
2542
     *
2543
     * @throws \Doctrine\ORM\NonUniqueResultException
2544
     *
2545
     * @return Category|null
2546
     */
2547
    public static function findByCertificate($id)
2548
    {
2549
        $category = Database::getManager()
2550
            ->createQuery('SELECT c.catId FROM ChamiloCoreBundle:GradebookCertificate c WHERE c.id = :id')
2551
            ->setParameters(['id' => $id])
2552
            ->getOneOrNullResult();
2553
2554
        if (empty($category)) {
2555
            return null;
2556
        }
2557
2558
        $category = self::load($category['catId']);
2559
2560
        if (empty($category)) {
2561
            return null;
2562
        }
2563
2564
        return $category[0];
2565
    }
2566
2567
    /**
2568
     * @param int $value
2569
     */
2570
    public function setDocumentId($value)
2571
    {
2572
        $this->documentId = (int) $value;
2573
    }
2574
2575
    /**
2576
     * @return int
2577
     */
2578
    public function getDocumentId()
2579
    {
2580
        return $this->documentId;
2581
    }
2582
2583
    /**
2584
     * Get the remaining weight in root category.
2585
     *
2586
     * @return int
2587
     */
2588
    public function getRemainingWeight()
2589
    {
2590
        $subCategories = $this->get_subcategories();
2591
2592
        $subWeight = 0;
2593
2594
        /** @var Category $subCategory */
2595
        foreach ($subCategories as $subCategory) {
2596
            $subWeight += $subCategory->get_weight();
2597
        }
2598
2599
        return $this->weight - $subWeight;
2600
    }
2601
2602
    /**
2603
     * @return Category
2604
     */
2605
    private static function create_root_category()
2606
    {
2607
        $cat = new Category();
2608
        $cat->set_id(0);
2609
        $cat->set_name(get_lang('RootCat'));
2610
        $cat->set_description(null);
2611
        $cat->set_user_id(0);
2612
        $cat->set_course_code(null);
2613
        $cat->set_parent_id(null);
2614
        $cat->set_weight(0);
2615
        $cat->set_visible(1);
2616
        $cat->setGenerateCertificates(0);
2617
        $cat->setIsRequirement(false);
2618
2619
        return $cat;
2620
    }
2621
2622
    /**
2623
     * @param Doctrine\DBAL\Driver\Statement|null $result
2624
     *
2625
     * @return array
2626
     */
2627
    private static function create_category_objects_from_sql_result($result)
2628
    {
2629
        $categories = [];
2630
        $allow = api_get_configuration_value('allow_gradebook_stats');
2631
        if ($allow) {
2632
            $em = Database::getManager();
2633
            $repo = $em->getRepository('ChamiloCoreBundle:GradebookCategory');
2634
        }
2635
2636
        while ($data = Database::fetch_array($result)) {
2637
            $cat = new Category();
2638
            $cat->set_id($data['id']);
2639
            $cat->set_name($data['name']);
2640
            $cat->set_description($data['description']);
2641
            $cat->set_user_id($data['user_id']);
2642
            $cat->set_course_code($data['course_code']);
2643
            $cat->set_parent_id($data['parent_id']);
2644
            $cat->set_weight($data['weight']);
2645
            $cat->set_visible($data['visible']);
2646
            $cat->set_session_id($data['session_id']);
2647
            $cat->set_certificate_min_score($data['certif_min_score']);
2648
            $cat->set_grade_model_id($data['grade_model_id']);
2649
            $cat->set_locked($data['locked']);
2650
            $cat->setGenerateCertificates($data['generate_certificates']);
2651
            $cat->setIsRequirement($data['is_requirement']);
2652
            $cat->setCourseListDependency(isset($data['depends']) ? $data['depends'] : []);
2653
            $cat->setMinimumToValidate(isset($data['minimum_to_validate']) ? $data['minimum_to_validate'] : null);
2654
            $cat->setGradeBooksToValidateInDependence(isset($data['gradebooks_to_validate_in_dependence']) ? $data['gradebooks_to_validate_in_dependence'] : null);
2655
            $cat->setDocumentId($data['document_id']);
2656
            if ($allow) {
2657
                $cat->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...
2658
            }
2659
2660
            $categories[] = $cat;
2661
        }
2662
2663
        return $categories;
2664
    }
2665
2666
    /**
2667
     * Internal function used by get_target_categories().
2668
     *
2669
     * @param array $targets
2670
     * @param int   $level
2671
     * @param int   $catid
2672
     *
2673
     * @return array
2674
     */
2675
    private function addTargetSubcategories($targets, $level, $catid)
2676
    {
2677
        $subcats = self::load(null, null, null, $catid);
2678
        foreach ($subcats as $cat) {
2679
            if ($this->can_be_moved_to_cat($cat)) {
2680
                $targets[] = [
2681
                    $cat->get_id(),
2682
                    $cat->get_name(),
2683
                    $level + 1,
2684
                ];
2685
                $targets = $this->addTargetSubcategories(
2686
                    $targets,
2687
                    $level + 1,
2688
                    $cat->get_id()
2689
                );
2690
            }
2691
        }
2692
2693
        return $targets;
2694
    }
2695
2696
    /**
2697
     * Internal function used by get_target_categories() and addTargetSubcategories()
2698
     * Can this category be moved to the given category ?
2699
     * Impossible when origin and target are the same... children won't be processed
2700
     * either. (a category can't be moved to one of its own children).
2701
     */
2702
    private function can_be_moved_to_cat($cat)
2703
    {
2704
        return $cat->get_id() != $this->get_id();
2705
    }
2706
2707
    /**
2708
     * Internal function used by move_to_cat().
2709
     */
2710
    private function applyCourseCodeToChildren()
2711
    {
2712
        $cats = self::load(null, null, null, $this->id, null);
2713
        $evals = Evaluation::load(null, null, null, $this->id, null);
2714
        $links = LinkFactory::load(
2715
            null,
2716
            null,
2717
            null,
2718
            null,
2719
            null,
2720
            $this->id,
2721
            null
2722
        );
2723
        /** @var Category $cat */
2724
        foreach ($cats as $cat) {
2725
            $cat->set_course_code($this->get_course_code());
2726
            $cat->save();
2727
            $cat->applyCourseCodeToChildren();
2728
        }
2729
2730
        foreach ($evals as $eval) {
2731
            $eval->set_course_code($this->get_course_code());
2732
            $eval->save();
2733
        }
2734
2735
        foreach ($links as $link) {
2736
            $link->delete();
2737
        }
2738
    }
2739
2740
    /**
2741
     * Internal function used by get_tree().
2742
     *
2743
     * @param int      $level
2744
     * @param int|null $visible
2745
     *
2746
     * @return array
2747
     */
2748
    private function add_subtree($targets, $level, $catid, $visible)
2749
    {
2750
        $subcats = self::load(null, null, null, $catid, $visible);
2751
2752
        if (!empty($subcats)) {
2753
            foreach ($subcats as $cat) {
2754
                $targets[] = [
2755
                    $cat->get_id(),
2756
                    $cat->get_name(),
2757
                    $level + 1,
2758
                ];
2759
                $targets = self::add_subtree(
0 ignored issues
show
Bug Best Practice introduced by
The method Category::add_subtree() 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

2759
                /** @scrutinizer ignore-call */ 
2760
                $targets = self::add_subtree(
Loading history...
2760
                    $targets,
2761
                    $level + 1,
2762
                    $cat->get_id(),
2763
                    $visible
2764
                );
2765
            }
2766
        }
2767
2768
        return $targets;
2769
    }
2770
2771
    /**
2772
     * Calculate the current score on a gradebook category for a user.
2773
     *
2774
     * @param int      $userId   The user id
2775
     * @param Category $category The gradebook category
2776
     *
2777
     * @return float The score
2778
     */
2779
    private static function calculateCurrentScore(
2780
        $userId,
2781
        $category
2782
    ) {
2783
        if (empty($category)) {
2784
            return 0;
2785
        }
2786
        $courseEvaluations = $category->get_evaluations(
2787
            $userId,
2788
            true
2789
        );
2790
        $courseLinks = $category->get_links($userId, true);
2791
        $evaluationsAndLinks = array_merge($courseEvaluations, $courseLinks);
2792
        $categoryScore = 0;
2793
        for ($i = 0; $i < count($evaluationsAndLinks); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2794
            $item = $evaluationsAndLinks[$i];
2795
            $score = $item->calc_score($userId);
2796
            $itemValue = 0;
2797
            if (!empty($score)) {
2798
                $divider = $score[1] == 0 ? 1 : $score[1];
2799
                $itemValue = $score[0] / $divider * $item->get_weight();
2800
            }
2801
2802
            $categoryScore += $itemValue;
2803
        }
2804
2805
        return api_float_val($categoryScore);
2806
    }
2807
}
2808