Completed
Push — master ( c34076...6cb4af )
by Julito
11:15
created

Category   F

Complexity

Total Complexity 385

Size/Duplication

Total Lines 2820
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1285
dl 0
loc 2820
rs 0.8
c 0
b 0
f 0
wmc 385

99 Methods

Rating   Name   Duplication   Size   Complexity  
A lock() 0 6 1
A find_category() 0 10 3
A setCourseListDependency() 0 8 2
C add() 0 74 12
A get_description() 0 3 1
A set_course_code() 0 3 1
A get_user_id() 0 3 1
F load() 0 100 19
A hasEvaluationsWithStudentResults() 0 25 5
A is_visible() 0 3 1
A get_icon_name() 0 3 1
B get_target_categories() 0 49 9
B loadSessionCategories() 0 32 7
C get_evaluations() 0 94 17
A updateChildrenWeight() 0 11 4
B exportAllCertificates() 0 46 8
A setGenerateCertificates() 0 3 1
A get_type() 0 3 1
B get_subcategories() 0 81 10
F calc_score() 0 282 66
A set_locked() 0 3 1
A show_message_resource_delete() 0 14 2
D generateUserCertificate() 0 168 16
A get_item_type() 0 3 1
A get_date() 0 3 1
A get_tree() 0 43 5
A showAllCategoryInfo() 0 14 2
A get_all_courses() 0 18 3
A getCertificateMinScore() 0 7 2
A __construct() 0 19 1
A setMinimumToValidate() 0 3 1
A set_parent_id() 0 3 1
A set_skills() 0 3 1
A getCategories() 0 11 1
A set_description() 0 3 1
A getMinimumToValidate() 0 3 1
A getIsRequirement() 0 3 1
A get_skills() 0 11 2
A get_course_code() 0 3 1
A delete() 0 5 1
A get_not_created_course_categories() 0 32 3
A is_certificate_available() 0 20 4
A getCourseListDependency() 0 3 1
A set_grade_model_id() 0 3 1
A set_weight() 0 3 1
A is_movable() 0 3 3
A set_session_id() 0 3 1
A getGenerateCertificates() 0 3 1
A createCategoryObjectFromEntity() 0 19 1
A deleteAllCertificates() 0 12 4
B apply_visibility_to_children() 0 30 7
B get_links() 0 57 11
A is_course() 0 4 4
A move_to_cat() 0 8 2
A get_parent_id() 0 3 1
B delete_all() 0 44 7
B lockAllItems() 0 23 7
A set_user_id() 0 3 1
A set_visible() 0 3 1
C get_root_categories_for_student() 0 82 12
A get_id() 0 3 1
A get_weight() 0 3 1
A get_grade_model_id() 0 7 2
A deleteFromCourse() 0 25 4
A get_session_id() 0 3 1
A setIsRequirement() 0 3 1
A set_id() 0 3 1
A is_locked() 0 3 3
B getIndependentCategoriesWithStudentResult() 0 25 7
A getSkillsForSelect() 0 11 3
A generateCertificatesInUserList() 0 5 3
A get_name() 0 3 1
A get_root_categories_for_teacher() 0 46 5
A set_certificate_min_score() 0 3 1
B save() 0 73 10
A set_name() 0 3 1
A setDocumentId() 0 3 1
A create_root_category() 0 15 1
A getRemainingWeight() 0 12 2
A setUrl() 0 11 3
B create_category_objects_from_sql_result() 0 39 7
A calculateCurrentScore() 0 27 5
A getCourseId() 0 3 1
A addTargetSubcategories() 0 19 3
A setGradeBooksToValidateInDependence() 0 5 1
A setCourseId() 0 5 1
A can_be_moved_to_cat() 0 3 1
A getStudentList() 0 3 1
A applyCourseCodeToChildren() 0 27 4
A getGradeBooksToValidateInDependence() 0 3 1
A getCurrentScore() 0 34 4
A add_subtree() 0 21 3
A setStudentList() 0 3 1
A getUrl() 0 14 3
A getDocumentId() 0 3 1
A getDownloadCertificateBlock() 0 18 2
A registerCurrentScore() 0 9 1
A userFinishedCourse() 0 18 2
A findByCertificate() 0 18 3

How to fix   Complexity   

Complex Class

Complex classes like Category often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Category, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\GradebookCategory;
5
use ChamiloSession as Session;
6
7
/**
8
 * Class Category
9
 * Defines a gradebook Category object.
10
 */
11
class Category implements GradebookItem
12
{
13
    public $studentList;
14
    public $evaluations;
15
    public $links;
16
    public $subCategories;
17
    /** @var GradebookCategory */
18
    public $entity;
19
    private $id;
20
    private $name;
21
    private $description;
22
    private $user_id;
23
    private $course_code;
24
    private $courseId;
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 = '';
50
        $this->courseId = 0;
51
        $this->parent = 0;
52
        $this->weight = 0;
53
        $this->visible = false;
54
        $this->certificate_min_score = 0;
55
        $this->session_id = 0;
56
        $this->grade_model_id = 0;
57
        $this->generateCertificates = false;
58
        $this->isRequirement = false;
59
        $this->courseDependency = [];
60
        $this->documentId = 0;
61
        $this->minimumToValidate = null;
62
    }
63
64
    /**
65
     * @return int
66
     */
67
    public function get_id()
68
    {
69
        return $this->id;
70
    }
71
72
    /**
73
     * @return string
74
     */
75
    public function get_name()
76
    {
77
        return $this->name;
78
    }
79
80
    /**
81
     * @return string
82
     */
83
    public function get_description()
84
    {
85
        return $this->description;
86
    }
87
88
    /**
89
     * @return int
90
     */
91
    public function get_user_id()
92
    {
93
        return $this->user_id;
94
    }
95
96
    /**
97
     * @return int|null
98
     */
99
    public function getCertificateMinScore()
100
    {
101
        if (!empty($this->certificate_min_score)) {
102
            return $this->certificate_min_score;
103
        }
104
105
        return null;
106
    }
107
108
    /**
109
     * @return string
110
     */
111
    public function get_course_code()
112
    {
113
        return $this->course_code;
114
    }
115
116
    /**
117
     * @return int
118
     */
119
    public function get_parent_id()
120
    {
121
        return $this->parent;
122
    }
123
124
    /**
125
     * @return int
126
     */
127
    public function get_weight()
128
    {
129
        return $this->weight;
130
    }
131
132
    /**
133
     * @return bool
134
     */
135
    public function is_locked()
136
    {
137
        return isset($this->locked) && 1 == $this->locked ? true : false;
138
    }
139
140
    /**
141
     * @return bool
142
     */
143
    public function is_visible()
144
    {
145
        return $this->visible;
146
    }
147
148
    /**
149
     * Get $isRequirement.
150
     *
151
     * @return int
152
     */
153
    public function getIsRequirement()
154
    {
155
        return $this->isRequirement;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->isRequirement returns the type boolean which is incompatible with the documented return type integer.
Loading history...
156
    }
157
158
    /**
159
     * @param int $id
160
     */
161
    public function set_id($id)
162
    {
163
        $this->id = $id;
164
    }
165
166
    /**
167
     * @param string $name
168
     */
169
    public function set_name($name)
170
    {
171
        $this->name = $name;
172
    }
173
174
    /**
175
     * @param string $description
176
     */
177
    public function set_description($description)
178
    {
179
        $this->description = $description;
180
    }
181
182
    /**
183
     * @param int $user_id
184
     */
185
    public function set_user_id($user_id)
186
    {
187
        $this->user_id = $user_id;
188
    }
189
190
    /**
191
     * @param string $course_code
192
     */
193
    public function set_course_code($course_code)
194
    {
195
        $this->course_code = $course_code;
196
    }
197
198
    /**
199
     * @param float $min_score
200
     */
201
    public function set_certificate_min_score($min_score = null)
202
    {
203
        $this->certificate_min_score = $min_score;
204
    }
205
206
    /**
207
     * @param int $parent
208
     */
209
    public function set_parent_id($parent)
210
    {
211
        $this->parent = (int) $parent;
212
    }
213
214
    /**
215
     * Filters to int and sets the session ID.
216
     *
217
     * @param   int     The session ID from the Dokeos course session
218
     */
219
    public function set_session_id($session_id = 0)
220
    {
221
        $this->session_id = (int) $session_id;
222
    }
223
224
    /**
225
     * @param $weight
226
     */
227
    public function set_weight($weight)
228
    {
229
        $this->weight = $weight;
230
    }
231
232
    /**
233
     * @param $visible
234
     */
235
    public function set_visible($visible)
236
    {
237
        $this->visible = $visible;
238
    }
239
240
    /**
241
     * @param int $id
242
     */
243
    public function set_grade_model_id($id)
244
    {
245
        $this->grade_model_id = $id;
246
    }
247
248
    /**
249
     * @param $locked
250
     */
251
    public function set_locked($locked)
252
    {
253
        $this->locked = $locked;
0 ignored issues
show
Bug Best Practice introduced by
The property locked does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
254
    }
255
256
    /**
257
     * Set $isRequirement.
258
     *
259
     * @param int $isRequirement
260
     */
261
    public function setIsRequirement($isRequirement)
262
    {
263
        $this->isRequirement = $isRequirement;
264
    }
265
266
    /**
267
     * @param $value
268
     */
269
    public function setCourseListDependency($value)
270
    {
271
        $this->courseDependency = [];
272
273
        $unserialized = UnserializeApi::unserialize('not_allowed_classes', $value, true);
274
275
        if (false !== $unserialized) {
276
            $this->courseDependency = $unserialized;
277
        }
278
    }
279
280
    /**
281
     * Course id list.
282
     *
283
     * @return array
284
     */
285
    public function getCourseListDependency()
286
    {
287
        return $this->courseDependency;
288
    }
289
290
    /**
291
     * @param int $value
292
     */
293
    public function setMinimumToValidate($value)
294
    {
295
        $this->minimumToValidate = $value;
296
    }
297
298
    public function getMinimumToValidate()
299
    {
300
        return $this->minimumToValidate;
301
    }
302
303
    /**
304
     * @return int|null
305
     */
306
    public function get_grade_model_id()
307
    {
308
        if ($this->grade_model_id < 0) {
309
            return null;
310
        }
311
312
        return $this->grade_model_id;
313
    }
314
315
    /**
316
     * @return string
317
     */
318
    public function get_type()
319
    {
320
        return 'category';
321
    }
322
323
    /**
324
     * @param bool $from_db
325
     *
326
     * @return array|resource
327
     */
328
    public function get_skills($from_db = true)
329
    {
330
        if ($from_db) {
331
            $categoryId = $this->get_id();
332
            $gradebook = new Gradebook();
333
            $skills = $gradebook->getSkillsByGradebook($categoryId);
334
        } else {
335
            $skills = $this->skills;
336
        }
337
338
        return $skills;
339
    }
340
341
    /**
342
     * @return array
343
     */
344
    public function getSkillsForSelect()
345
    {
346
        $skills = $this->get_skills();
347
        $skill_select = [];
348
        if (!empty($skills)) {
349
            foreach ($skills as $skill) {
350
                $skill_select[$skill['id']] = $skill['name'];
351
            }
352
        }
353
354
        return $skill_select;
355
    }
356
357
    /**
358
     * Set the generate_certificates value.
359
     *
360
     * @param int $generateCertificates
361
     */
362
    public function setGenerateCertificates($generateCertificates)
363
    {
364
        $this->generateCertificates = $generateCertificates;
365
    }
366
367
    /**
368
     * Get the generate_certificates value.
369
     *
370
     * @return int
371
     */
372
    public function getGenerateCertificates()
373
    {
374
        return $this->generateCertificates;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->generateCertificates returns the type boolean which is incompatible with the documented return type integer.
Loading history...
375
    }
376
377
    /**
378
     * @param int $id
379
     * @param int $session_id
380
     *
381
     * @return array
382
     */
383
    public static function loadSessionCategories(
384
        $id = null,
385
        $session_id = null
386
    ) {
387
        if (isset($id) && 0 === (int) $id) {
388
            $cats = [];
389
            $cats[] = self::create_root_category();
390
391
            return $cats;
392
        }
393
        $courseId = api_get_course_int_id();
394
        $courseInfo = api_get_course_info_by_id($courseId);
395
        $courseCode = $courseInfo['code'];
396
        $session_id = (int) $session_id;
397
398
        if (!empty($session_id)) {
399
            $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
400
            $sql = 'SELECT id, c_id
401
                    FROM '.$table.'
402
                    WHERE session_id = '.$session_id;
403
            $result_session = Database::query($sql);
404
            if (Database::num_rows($result_session) > 0) {
405
                $categoryList = [];
406
                while ($data_session = Database::fetch_array($result_session)) {
407
                    $parent_id = $data_session['id'];
408
                    if ($data_session['c_id'] == $courseId) {
409
                        $categories = self::load($parent_id);
410
                        $categoryList = array_merge($categoryList, $categories);
411
                    }
412
                }
413
414
                return $categoryList;
415
            }
416
        }
417
    }
418
419
    /**
420
     * Retrieve categories and return them as an array of Category objects.
421
     *
422
     * @param int    $id          category id
423
     * @param int    $user_id     (category owner)
424
     * @param string $course_code
425
     * @param int    $parent_id   parent category
426
     * @param bool   $visible
427
     * @param int    $session_id  (in case we are in a session)
428
     * @param bool   $order_by    Whether to show all "session"
429
     *                            categories (true) or hide them (false) in case there is no session id
430
     *
431
     * @return array
432
     */
433
    public static function load(
434
        $id = null,
435
        $user_id = null,
436
        $course_code = null,
437
        $parent_id = null,
438
        $visible = null,
439
        $session_id = null,
440
        $order_by = null
441
    ) {
442
        //if the category given is explicitly 0 (not null), then create
443
        // a root category object (in memory)
444
        if (isset($id) && 0 === (int) $id) {
445
            $cats = [];
446
            $cats[] = self::create_root_category();
447
448
            return $cats;
449
        }
450
451
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
452
        $sql = 'SELECT * FROM '.$table;
453
        $paramcount = 0;
454
        if (isset($id)) {
455
            $sql .= ' WHERE id = '.intval($id);
456
            $paramcount++;
457
        }
458
459
        if (isset($user_id)) {
460
            $user_id = intval($user_id);
461
            if (0 != $paramcount) {
462
                $sql .= ' AND';
463
            } else {
464
                $sql .= ' WHERE';
465
            }
466
            $sql .= ' user_id = '.intval($user_id);
467
            $paramcount++;
468
        }
469
470
        if (isset($course_code)) {
471
            if (0 != $paramcount) {
472
                $sql .= ' AND';
473
            } else {
474
                $sql .= ' WHERE';
475
            }
476
477
            if ('0' == $course_code) {
478
                $sql .= ' c_id is null ';
479
            } else {
480
                $courseInfo = api_get_course_info($course_code);
481
                if ($courseInfo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $courseInfo of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
482
                    $sql .= " c_id = '".intval($courseInfo['real_id'])."'";
483
                }
484
            }
485
486
            /*if ($show_session_categories !== true) {
487
                // a query on the course should show all
488
                // the categories inside sessions for this course
489
                // otherwise a special parameter is given to ask explicitely
490
                $sql .= " AND (session_id IS NULL OR session_id = 0) ";
491
            } else {*/
492
            if (empty($session_id)) {
493
                $sql .= ' AND (session_id IS NULL OR session_id = 0) ';
494
            } else {
495
                $sql .= ' AND session_id = '.(int) $session_id.' ';
496
            }
497
            //}
498
            $paramcount++;
499
        }
500
501
        if (isset($parent_id)) {
502
            if (0 != $paramcount) {
503
                $sql .= ' AND ';
504
            } else {
505
                $sql .= ' WHERE ';
506
            }
507
            $sql .= ' parent_id = '.intval($parent_id);
508
            $paramcount++;
509
        }
510
511
        if (isset($visible)) {
512
            if (0 != $paramcount) {
513
                $sql .= ' AND';
514
            } else {
515
                $sql .= ' WHERE';
516
            }
517
            $sql .= ' visible = '.intval($visible);
518
        }
519
520
        if (!empty($order_by)) {
521
            if (!empty($order_by) && '' != $order_by) {
522
                $sql .= ' '.$order_by;
523
            }
524
        }
525
526
        $result = Database::query($sql);
527
        $categories = [];
528
        if (Database::num_rows($result) > 0) {
529
            $categories = self::create_category_objects_from_sql_result($result);
530
        }
531
532
        return $categories;
533
    }
534
535
    /**
536
     * Create a category object from a GradebookCategory entity.
537
     *
538
     * @param GradebookCategory $gradebookCategory The entity
539
     *
540
     * @return \Category
541
     */
542
    public static function createCategoryObjectFromEntity(GradebookCategory $gradebookCategory)
543
    {
544
        $category = new Category();
545
        $category->set_id($gradebookCategory->getId());
546
        $category->set_name($gradebookCategory->getName());
547
        $category->set_description($gradebookCategory->getDescription());
548
        $category->set_user_id($gradebookCategory->getUserId());
0 ignored issues
show
Bug introduced by
The method getUserId() does not exist on Chamilo\CoreBundle\Entity\GradebookCategory. Did you maybe mean getUser()? ( Ignorable by Annotation )

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

548
        $category->set_user_id($gradebookCategory->/** @scrutinizer ignore-call */ getUserId());

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...
549
        $category->set_course_code($gradebookCategory->getCourseCode());
0 ignored issues
show
Bug introduced by
The method getCourseCode() does not exist on Chamilo\CoreBundle\Entity\GradebookCategory. Did you maybe mean getCourse()? ( Ignorable by Annotation )

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

549
        $category->set_course_code($gradebookCategory->/** @scrutinizer ignore-call */ getCourseCode());

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...
550
        $category->set_parent_id($gradebookCategory->getParentId());
551
        $category->set_weight($gradebookCategory->getWeight());
552
        $category->set_visible($gradebookCategory->getVisible());
553
        $category->set_session_id($gradebookCategory->getSessionId());
554
        $category->set_certificate_min_score($gradebookCategory->getCertifMinScore());
555
        $category->set_grade_model_id($gradebookCategory->getGradeModelId());
556
        $category->set_locked($gradebookCategory->getLocked());
557
        $category->setGenerateCertificates($gradebookCategory->getGenerateCertificates());
558
        $category->setIsRequirement($gradebookCategory->getIsRequirement());
559
560
        return $category;
561
    }
562
563
    /**
564
     * Insert this category into the database.
565
     */
566
    public function add()
567
    {
568
        if (isset($this->name) && '-1' == $this->name) {
569
            return false;
570
        }
571
572
        if (isset($this->name) && isset($this->user_id)) {
573
            $em = Database::getManager();
574
575
            $courseInfo = api_get_course_info($this->course_code);
576
            $course = api_get_course_entity($courseInfo['real_id']);
577
578
            $category = new GradebookCategory();
579
            $category->setName($this->name);
580
            $category->setDescription($this->description);
581
            $category->setUser(api_get_user_entity($this->user_id));
582
            $category->setCourse($course);
583
            $category->setParentId($this->parent);
584
            $category->setWeight($this->weight);
585
            $category->setVisible($this->visible);
586
            $category->setCertifMinScore($this->certificate_min_score);
587
            $category->setSessionId($this->session_id);
588
            $category->setGenerateCertificates($this->generateCertificates);
589
            $category->setGradeModelId($this->grade_model_id);
590
            $category->setIsRequirement($this->isRequirement);
591
            $category->setLocked(false);
592
593
            $em->persist($category);
594
            $em->flush();
595
596
            $id = $category->getId();
597
            $this->set_id($id);
598
599
            if (!empty($id)) {
600
                $parent_id = $this->get_parent_id();
601
                $grade_model_id = $this->get_grade_model_id();
602
                if (0 == $parent_id) {
603
                    //do something
604
                    if (isset($grade_model_id) &&
605
                        !empty($grade_model_id) &&
606
                        '-1' != $grade_model_id
607
                    ) {
608
                        $obj = new GradeModel();
609
                        $components = $obj->get_components($grade_model_id);
610
                        $default_weight_setting = api_get_setting('gradebook_default_weight');
611
                        $default_weight = 100;
612
                        if (isset($default_weight_setting)) {
613
                            $default_weight = $default_weight_setting;
614
                        }
615
                        foreach ($components as $component) {
616
                            $gradebook = new Gradebook();
617
                            $params = [];
618
619
                            $params['name'] = $component['acronym'];
620
                            $params['description'] = $component['title'];
621
                            $params['user_id'] = api_get_user_id();
622
                            $params['parent_id'] = $id;
623
                            $params['weight'] = $component['percentage'] / 100 * $default_weight;
624
                            $params['session_id'] = api_get_session_id();
625
                            $params['course_code'] = $this->get_course_code();
626
627
                            $gradebook->save($params);
628
                        }
629
                    }
630
                }
631
            }
632
633
            $gradebook = new Gradebook();
634
            $gradebook->updateSkillsToGradeBook(
635
                $this->id,
636
                $this->get_skills(false)
637
            );
638
639
            return $id;
640
        }
641
    }
642
643
    /**
644
     * Update the properties of this category in the database.
645
     *
646
     * @todo fix me
647
     */
648
    public function save()
649
    {
650
        $em = Database::getManager();
651
652
        /** @var GradebookCategory $gradebookCategory */
653
        $gradebookCategory = $em
654
            ->getRepository('ChamiloCoreBundle:GradebookCategory')
655
            ->find($this->id);
656
657
        if (empty($gradebookCategory)) {
658
            return false;
659
        }
660
661
        $course = api_get_user_course_entity();
0 ignored issues
show
Bug introduced by
The function api_get_user_course_entity was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

661
        $course = /** @scrutinizer ignore-call */ api_get_user_course_entity();
Loading history...
662
663
        $gradebookCategory->setName($this->name);
664
        $gradebookCategory->setDescription($this->description);
665
        $gradebookCategory->setUserId($this->user_id);
0 ignored issues
show
Bug introduced by
The method setUserId() does not exist on Chamilo\CoreBundle\Entity\GradebookCategory. Did you maybe mean setUser()? ( Ignorable by Annotation )

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

665
        $gradebookCategory->/** @scrutinizer ignore-call */ 
666
                            setUserId($this->user_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...
666
        $gradebookCategory->setCourse($course);
667
        //$gradebookCategory->setCourseCode($this->course_code);
668
        $gradebookCategory->setParentId($this->parent);
669
        $gradebookCategory->setWeight($this->weight);
670
        $gradebookCategory->setVisible($this->visible);
671
        $gradebookCategory->setCertifMinScore($this->certificate_min_score);
672
        $gradebookCategory->setGenerateCertificates(
673
            $this->generateCertificates
674
        );
675
        $gradebookCategory->setGradeModelId($this->grade_model_id);
676
        $gradebookCategory->setIsRequirement($this->isRequirement);
677
678
        $em->merge($gradebookCategory);
679
        $em->flush();
680
681
        if (!empty($this->id)) {
682
            $parent_id = $this->get_parent_id();
683
            $grade_model_id = $this->get_grade_model_id();
684
            if (0 == $parent_id) {
685
                if (isset($grade_model_id) &&
686
                    !empty($grade_model_id) &&
687
                    '-1' != $grade_model_id
688
                ) {
689
                    $obj = new GradeModel();
690
                    $components = $obj->get_components($grade_model_id);
691
                    $default_weight_setting = api_get_setting('gradebook_default_weight');
692
                    $default_weight = 100;
693
                    if (isset($default_weight_setting)) {
694
                        $default_weight = $default_weight_setting;
695
                    }
696
                    $final_weight = $this->get_weight();
697
                    if (!empty($final_weight)) {
698
                        $default_weight = $this->get_weight();
699
                    }
700
                    foreach ($components as $component) {
701
                        $gradebook = new Gradebook();
702
                        $params = [];
703
                        $params['name'] = $component['acronym'];
704
                        $params['description'] = $component['title'];
705
                        $params['user_id'] = api_get_user_id();
706
                        $params['parent_id'] = $this->id;
707
                        $params['weight'] = $component['percentage'] / 100 * $default_weight;
708
                        $params['session_id'] = api_get_session_id();
709
                        $params['course_code'] = $this->get_course_code();
710
                        $gradebook->save($params);
711
                    }
712
                }
713
            }
714
        }
715
716
        $gradebook = new Gradebook();
717
        $gradebook->updateSkillsToGradeBook(
718
            $this->id,
719
            $this->get_skills(false),
720
            true
721
        );
722
    }
723
724
    /**
725
     * Update link weights see #5168.
726
     *
727
     * @param type $new_weight
728
     */
729
    public function updateChildrenWeight($new_weight)
730
    {
731
        $links = $this->get_links();
732
        $old_weight = $this->get_weight();
733
734
        if (!empty($links)) {
735
            foreach ($links as $link_item) {
736
                if (isset($link_item)) {
737
                    $new_item_weight = $new_weight * $link_item->get_weight() / $old_weight;
738
                    $link_item->set_weight($new_item_weight);
739
                    $link_item->save();
740
                }
741
            }
742
        }
743
    }
744
745
    /**
746
     * Delete this evaluation from the database.
747
     */
748
    public function delete()
749
    {
750
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
751
        $sql = 'DELETE FROM '.$table.' WHERE id = '.intval($this->id);
752
        Database::query($sql);
753
    }
754
755
    /**
756
     * Delete the gradebook categories from a course, including course sessions.
757
     *
758
     * @param \Chamilo\CoreBundle\Entity\Course $course
759
     */
760
    public static function deleteFromCourse($course)
761
    {
762
        $em = Database::getManager();
763
        $categories = $em
764
            ->createQuery(
765
                'SELECT DISTINCT gc.sessionId
766
                FROM ChamiloCoreBundle:GradebookCategory gc WHERE gc.course = :course'
767
            )
768
            ->setParameter('course', $course)
769
            ->getResult();
770
771
        foreach ($categories as $category) {
772
            $cats = self::load(
773
                null,
774
                null,
775
                $course->getCode(),
776
                null,
777
                null,
778
                (int) $category['sessionId']
779
            );
780
781
            if (!empty($cats)) {
782
                /** @var self $cat */
783
                foreach ($cats as $cat) {
784
                    $cat->delete_all();
785
                }
786
            }
787
        }
788
    }
789
790
    /**
791
     * @param int $course_id
792
     *
793
     * @return bool|string
794
     */
795
    public function show_message_resource_delete($course_id)
796
    {
797
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
798
        $sql = 'SELECT count(*) AS num 
799
                FROM '.$table.'
800
                WHERE
801
                    c_id = "'.Database::escape_string($course_id).'" AND
802
                    visible = 3';
803
        $res = Database::query($sql);
804
        $option = Database::fetch_array($res, 'ASSOC');
805
        if ($option['num'] >= 1) {
806
            return '&nbsp;&nbsp;<span class="resource-deleted">(&nbsp;'.get_lang('The resource has been deleted').'&nbsp;)</span>';
807
        } else {
808
            return false;
809
        }
810
    }
811
812
    /**
813
     * Shows all information of an category.
814
     *
815
     * @param int $categoryId
816
     *
817
     * @return array
818
     */
819
    public function showAllCategoryInfo($categoryId)
820
    {
821
        $categoryId = (int) $categoryId;
822
        if (empty($categoryId)) {
823
            return [];
824
        }
825
826
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
827
        $sql = 'SELECT * FROM '.$table.'
828
                WHERE id = '.$categoryId;
829
        $result = Database::query($sql);
830
        $row = Database::fetch_array($result, 'ASSOC');
831
832
        return $row;
833
    }
834
835
    /**
836
     * Checks if the certificate is available for the given user in this category.
837
     *
838
     * @param int $user_id User ID
839
     *
840
     * @return bool True if conditions match, false if fails
841
     */
842
    public function is_certificate_available($user_id)
843
    {
844
        $score = $this->calc_score(
845
            $user_id,
846
            null,
847
            $this->course_code,
848
            $this->session_id
849
        );
850
851
        if (isset($score) && isset($score[0])) {
852
            // Get a percentage score to compare to minimum certificate score
853
            // $certification_score = $score[0] / $score[1] * 100;
854
            // Get real score not a percentage.
855
            $certification_score = $score[0];
856
            if ($certification_score >= $this->certificate_min_score) {
857
                return true;
858
            }
859
        }
860
861
        return false;
862
    }
863
864
    /**
865
     * Is this category a course ?
866
     * A category is a course if it has a course code and no parent category.
867
     */
868
    public function is_course()
869
    {
870
        return isset($this->course_code) && !empty($this->course_code)
871
            && (!isset($this->parent) || 0 == $this->parent);
872
    }
873
874
    /**
875
     * Calculate the score of this category.
876
     *
877
     * @param int    $stud_id     student id (default: all students - then the average is returned)
878
     * @param        $type
879
     * @param string $course_code
880
     * @param int    $session_id
881
     *
882
     * @return array (score sum, weight sum)
883
     *               or null if no scores available
884
     */
885
    public function calc_score(
886
        $stud_id = null,
887
        $type = null,
888
        $course_code = '',
889
        $session_id = null
890
    ) {
891
        $key = 'category:'.$this->id.'student:'.(int) $stud_id.'type:'.$type.'course:'.$course_code.'session:'.(int) $session_id;
892
        $useCache = api_get_configuration_value('gradebook_use_apcu_cache');
893
        $cacheAvailable = api_get_configuration_value('apc') && $useCache;
894
895
        if ($cacheAvailable) {
896
            $cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
897
            if ($cacheDriver->contains($key)) {
898
                return $cacheDriver->fetch($key);
899
            }
900
        }
901
        // Classic
902
        if (!empty($stud_id) && '' == $type) {
903
            if (!empty($course_code)) {
904
                $cats = $this->get_subcategories(
905
                    $stud_id,
906
                    $course_code,
907
                    $session_id
908
                );
909
                $evals = $this->get_evaluations($stud_id, false, $course_code);
910
                $links = $this->get_links($stud_id, false, $course_code);
911
            } else {
912
                $cats = $this->get_subcategories($stud_id);
913
                $evals = $this->get_evaluations($stud_id);
914
                $links = $this->get_links($stud_id);
915
            }
916
917
            // Calculate score
918
            $count = 0;
919
            $ressum = 0;
920
            $weightsum = 0;
921
922
            if (!empty($cats)) {
923
                /** @var Category $cat */
924
                foreach ($cats as $cat) {
925
                    $cat->set_session_id($session_id);
926
                    $cat->set_course_code($course_code);
927
                    $cat->setStudentList($this->getStudentList());
928
                    $score = $cat->calc_score(
929
                        $stud_id,
930
                        null,
931
                        $course_code,
932
                        $session_id
933
                    );
934
935
                    $catweight = 0;
936
                    if (0 != $cat->get_weight()) {
937
                        $catweight = $cat->get_weight();
938
                        $weightsum += $catweight;
939
                    }
940
941
                    if (isset($score) && !empty($score[1]) && !empty($catweight)) {
942
                        $ressum += $score[0] / $score[1] * $catweight;
943
                    }
944
                }
945
            }
946
947
            if (!empty($evals)) {
948
                /** @var Evaluation $eval */
949
                foreach ($evals as $eval) {
950
                    $eval->setStudentList($this->getStudentList());
951
                    $evalres = $eval->calc_score($stud_id);
952
                    if (isset($evalres) && 0 != $eval->get_weight()) {
953
                        $evalweight = $eval->get_weight();
954
                        $weightsum += $evalweight;
955
                        if (!empty($evalres[1])) {
956
                            $ressum += $evalres[0] / $evalres[1] * $evalweight;
957
                        }
958
                    } else {
959
                        if (0 != $eval->get_weight()) {
960
                            $evalweight = $eval->get_weight();
961
                            $weightsum += $evalweight;
962
                        }
963
                    }
964
                }
965
            }
966
967
            if (!empty($links)) {
968
                /** @var EvalLink|ExerciseLink $link */
969
                foreach ($links as $link) {
970
                    $link->setStudentList($this->getStudentList());
971
972
                    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...
973
                        $link->set_session_id($session_id);
974
                    }
975
976
                    $linkres = $link->calc_score($stud_id, null);
977
                    if (!empty($linkres) && 0 != $link->get_weight()) {
978
                        $linkweight = $link->get_weight();
979
                        $link_res_denom = 0 == $linkres[1] ? 1 : $linkres[1];
980
                        $weightsum += $linkweight;
981
                        $ressum += $linkres[0] / $link_res_denom * $linkweight;
982
                    } else {
983
                        // Adding if result does not exists
984
                        if (0 != $link->get_weight()) {
985
                            $linkweight = $link->get_weight();
986
                            $weightsum += $linkweight;
987
                        }
988
                    }
989
                }
990
            }
991
        } else {
992
            if (!empty($course_code)) {
993
                $cats = $this->get_subcategories(
994
                    null,
995
                    $course_code,
996
                    $session_id
997
                );
998
                $evals = $this->get_evaluations(null, false, $course_code);
999
                $links = $this->get_links(null, false, $course_code);
1000
            } else {
1001
                $cats = $this->get_subcategories(null);
1002
                $evals = $this->get_evaluations(null);
1003
                $links = $this->get_links(null);
1004
            }
1005
1006
            // Calculate score
1007
            $ressum = 0;
1008
            $weightsum = 0;
1009
            $bestResult = 0;
1010
            $totalScorePerStudent = [];
1011
1012
            if (!empty($cats)) {
1013
                /** @var Category $cat */
1014
                foreach ($cats as $cat) {
1015
                    $cat->setStudentList($this->getStudentList());
1016
                    $score = $cat->calc_score(
1017
                        null,
1018
                        $type,
1019
                        $course_code,
1020
                        $session_id
1021
                    );
1022
1023
                    $catweight = 0;
1024
                    if (0 != $cat->get_weight()) {
1025
                        $catweight = $cat->get_weight();
1026
                        $weightsum += $catweight;
1027
                    }
1028
1029
                    if (isset($score) && !empty($score[1]) && !empty($catweight)) {
1030
                        $ressum += $score[0] / $score[1] * $catweight;
1031
1032
                        if ($ressum > $bestResult) {
1033
                            $bestResult = $ressum;
1034
                        }
1035
                    }
1036
                }
1037
            }
1038
1039
            if (!empty($evals)) {
1040
                if ('best' === $type) {
1041
                    $studentList = $this->getStudentList();
1042
                    foreach ($studentList as $student) {
1043
                        $studentId = $student['user_id'];
1044
                        foreach ($evals as $eval) {
1045
                            $linkres = $eval->calc_score($studentId, null);
1046
                            $linkweight = $eval->get_weight();
1047
                            $link_res_denom = 0 == $linkres[1] ? 1 : $linkres[1];
1048
                            $ressum = $linkres[0] / $link_res_denom * $linkweight;
1049
1050
                            if (!isset($totalScorePerStudent[$studentId])) {
1051
                                $totalScorePerStudent[$studentId] = 0;
1052
                            }
1053
                            $totalScorePerStudent[$studentId] += $ressum;
1054
                        }
1055
                    }
1056
                } else {
1057
                    /** @var Evaluation $eval */
1058
                    foreach ($evals as $eval) {
1059
                        $evalres = $eval->calc_score(null, $type);
1060
                        $eval->setStudentList($this->getStudentList());
1061
1062
                        if (isset($evalres) && 0 != $eval->get_weight()) {
1063
                            $evalweight = $eval->get_weight();
1064
                            $weightsum += $evalweight;
1065
                            if (!empty($evalres[1])) {
1066
                                $ressum += $evalres[0] / $evalres[1] * $evalweight;
1067
                            }
1068
1069
                            if ($ressum > $bestResult) {
1070
                                $bestResult = $ressum;
1071
                            }
1072
                        } else {
1073
                            if (0 != $eval->get_weight()) {
1074
                                $evalweight = $eval->get_weight();
1075
                                $weightsum += $evalweight;
1076
                            }
1077
                        }
1078
                    }
1079
                }
1080
            }
1081
1082
            if (!empty($links)) {
1083
                $studentList = $this->getStudentList();
1084
                if ('best' === $type) {
1085
                    foreach ($studentList as $student) {
1086
                        $studentId = $student['user_id'];
1087
                        foreach ($links as $link) {
1088
                            $linkres = $link->calc_score($studentId, null);
1089
                            $linkweight = $link->get_weight();
1090
                            $link_res_denom = 0 == $linkres[1] ? 1 : $linkres[1];
1091
                            $ressum = $linkres[0] / $link_res_denom * $linkweight;
1092
1093
                            if (!isset($totalScorePerStudent[$studentId])) {
1094
                                $totalScorePerStudent[$studentId] = 0;
1095
                            }
1096
                            $totalScorePerStudent[$studentId] += $ressum;
1097
                        }
1098
                    }
1099
                } else {
1100
                    /** @var EvalLink|ExerciseLink $link */
1101
                    foreach ($links as $link) {
1102
                        $link->setStudentList($this->getStudentList());
1103
1104
                        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...
1105
                            $link->set_session_id($session_id);
1106
                        }
1107
1108
                        $linkres = $link->calc_score($stud_id, $type);
1109
1110
                        if (!empty($linkres) && 0 != $link->get_weight()) {
1111
                            $linkweight = $link->get_weight();
1112
                            $link_res_denom = 0 == $linkres[1] ? 1 : $linkres[1];
1113
1114
                            $weightsum += $linkweight;
1115
                            $ressum += $linkres[0] / $link_res_denom * $linkweight;
1116
                            if ($ressum > $bestResult) {
1117
                                $bestResult = $ressum;
1118
                            }
1119
                        } else {
1120
                            // Adding if result does not exists
1121
                            if (0 != $link->get_weight()) {
1122
                                $linkweight = $link->get_weight();
1123
                                $weightsum += $linkweight;
1124
                            }
1125
                        }
1126
                    }
1127
                }
1128
            }
1129
        }
1130
1131
        switch ($type) {
1132
            case 'best':
1133
                arsort($totalScorePerStudent);
1134
                $maxScore = current($totalScorePerStudent);
1135
1136
                return [$maxScore, $this->get_weight()];
1137
                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...
1138
            case 'average':
1139
                if (empty($ressum)) {
1140
                    if ($cacheAvailable) {
1141
                        $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...
1142
                    }
1143
1144
                    return null;
1145
                }
1146
1147
                if ($cacheAvailable) {
1148
                    $cacheDriver->save($key, [$ressum, $weightsum]);
1149
                }
1150
1151
                return [$ressum, $weightsum];
1152
                break;
1153
            case 'ranking':
1154
                // category ranking is calculated in gradebook_data_generator.class.php
1155
                // function get_data
1156
                return null;
1157
1158
                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...
1159
                break;
1160
            default:
1161
                if ($cacheAvailable) {
1162
                    $cacheDriver->save($key, [$ressum, $weightsum]);
1163
                }
1164
1165
                return [$ressum, $weightsum];
1166
                break;
1167
        }
1168
    }
1169
1170
    /**
1171
     * Delete this category and every subcategory, evaluation and result inside.
1172
     */
1173
    public function delete_all()
1174
    {
1175
        $cats = self::load(null, null, $this->course_code, $this->id, null);
1176
        $evals = Evaluation::load(
1177
            null,
1178
            null,
1179
            $this->course_code,
1180
            $this->id,
1181
            null
1182
        );
1183
1184
        $links = LinkFactory::load(
1185
            null,
1186
            null,
1187
            null,
1188
            null,
1189
            $this->course_code,
1190
            $this->id,
1191
            null
1192
        );
1193
1194
        if (!empty($cats)) {
1195
            /** @var Category $cat */
1196
            foreach ($cats as $cat) {
1197
                $cat->delete_all();
1198
                $cat->delete();
1199
            }
1200
        }
1201
1202
        if (!empty($evals)) {
1203
            /** @var Evaluation $eval */
1204
            foreach ($evals as $eval) {
1205
                $eval->delete_with_results();
1206
            }
1207
        }
1208
1209
        if (!empty($links)) {
1210
            /** @var AbstractLink $link */
1211
            foreach ($links as $link) {
1212
                $link->delete();
1213
            }
1214
        }
1215
1216
        $this->delete();
1217
    }
1218
1219
    /**
1220
     * Return array of Category objects where a student is subscribed to.
1221
     *
1222
     * @param int    $stud_id
1223
     * @param string $course_code
1224
     * @param int    $session_id
1225
     *
1226
     * @return array
1227
     */
1228
    public function get_root_categories_for_student(
1229
        $stud_id,
1230
        $course_code = null,
1231
        $session_id = null
1232
    ) {
1233
        $main_course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1234
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
1235
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1236
1237
        $course_code = Database::escape_string($course_code);
1238
        $session_id = (int) $session_id;
1239
1240
        $sql = "SELECT * FROM $table WHERE parent_id = 0";
1241
1242
        if (!api_is_allowed_to_edit()) {
1243
            $sql .= ' AND visible = 1';
1244
            //proceed with checks on optional parameters course & session
1245
            if (!empty($course_code)) {
1246
                // TODO: considering it highly improbable that a user would get here
1247
                // if he doesn't have the rights to view this course and this
1248
                // session, we don't check his registration to these, but this
1249
                // could be an improvement
1250
                if (!empty($session_id)) {
1251
                    $sql .= " AND course_code = '".$course_code."' AND session_id = ".$session_id;
1252
                } else {
1253
                    $sql .= " AND course_code = '".$course_code."' AND session_id is null OR session_id=0";
1254
                }
1255
            } else {
1256
                //no optional parameter, proceed as usual
1257
                $sql .= ' AND course_code in
1258
                     (
1259
                        SELECT c.code
1260
                        FROM '.$main_course_user_table.' cu INNER JOIN '.$courseTable.' c
1261
                        ON (cu.c_id = c.id)
1262
                        WHERE cu.user_id = '.intval($stud_id).'
1263
                        AND cu.status = '.STUDENT.'
1264
                    )';
1265
            }
1266
        } elseif (api_is_allowed_to_edit() && !api_is_platform_admin()) {
1267
            //proceed with checks on optional parameters course & session
1268
            if (!empty($course_code)) {
1269
                // TODO: considering it highly improbable that a user would get here
1270
                // if he doesn't have the rights to view this course and this
1271
                // session, we don't check his registration to these, but this
1272
                // could be an improvement
1273
                $sql .= " AND course_code  = '".$course_code."'";
1274
                if (!empty($session_id)) {
1275
                    $sql .= " AND session_id = ".$session_id;
1276
                } else {
1277
                    $sql .= 'AND session_id IS NULL OR session_id = 0';
1278
                }
1279
            } else {
1280
                $sql .= ' AND course_code IN
1281
                     (
1282
                        SELECT c.code
1283
                        FROM '.$main_course_user_table.' cu INNER JOIN '.$courseTable.' c
1284
                        ON (cu.c_id = c.id)
1285
                        WHERE
1286
                            cu.user_id = '.api_get_user_id().' AND
1287
                            cu.status = '.COURSEMANAGER.'
1288
                    )';
1289
            }
1290
        } elseif (api_is_platform_admin()) {
1291
            if (isset($session_id) && 0 != $session_id) {
1292
                $sql .= ' AND session_id='.$session_id;
1293
            } else {
1294
                $sql .= ' AND coalesce(session_id,0)=0';
1295
            }
1296
        }
1297
        $result = Database::query($sql);
1298
        $cats = self::create_category_objects_from_sql_result($result);
1299
1300
        // course independent categories
1301
        if (empty($course_code)) {
1302
            $cats = $this->getIndependentCategoriesWithStudentResult(
1303
                0,
1304
                $stud_id,
1305
                $cats
1306
            );
1307
        }
1308
1309
        return $cats;
1310
    }
1311
1312
    /**
1313
     * Return array of Category objects where a teacher is admin for.
1314
     *
1315
     * @param int    $user_id     (to return everything, use 'null' here)
1316
     * @param string $course_code (optional)
1317
     * @param int    $session_id  (optional)
1318
     *
1319
     * @return array
1320
     */
1321
    public function get_root_categories_for_teacher(
1322
        $user_id,
1323
        $course_code = null,
1324
        $session_id = null
1325
    ) {
1326
        if (null == $user_id) {
1327
            return self::load(null, null, $course_code, 0, null, $session_id);
1328
        }
1329
1330
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
1331
        $main_course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1332
        $tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1333
1334
        $sql = 'SELECT * FROM '.$tbl_grade_categories.'
1335
                WHERE parent_id = 0 ';
1336
        if (!empty($course_code)) {
1337
            $sql .= " AND course_code = '".Database::escape_string($course_code)."' ";
1338
            if (!empty($session_id)) {
1339
                $sql .= " AND session_id = ".(int) $session_id;
1340
            }
1341
        } else {
1342
            $sql .= ' AND course_code in
1343
                 (
1344
                    SELECT c.code
1345
                    FROM '.$main_course_user_table.' cu
1346
                    INNER JOIN '.$courseTable.' c
1347
                    ON (cu.c_id = c.id)
1348
                    WHERE user_id = '.intval($user_id).'
1349
                )';
1350
        }
1351
        $result = Database::query($sql);
1352
        $cats = self::create_category_objects_from_sql_result($result);
1353
        // course independent categories
1354
        if (isset($course_code)) {
1355
            $indcats = self::load(
1356
                null,
1357
                $user_id,
1358
                $course_code,
1359
                0,
1360
                null,
1361
                $session_id
1362
            );
1363
            $cats = array_merge($cats, $indcats);
1364
        }
1365
1366
        return $cats;
1367
    }
1368
1369
    /**
1370
     * Can this category be moved to somewhere else ?
1371
     * The root and courses cannot be moved.
1372
     *
1373
     * @return bool
1374
     */
1375
    public function is_movable()
1376
    {
1377
        return !(!isset($this->id) || 0 == $this->id || $this->is_course());
1378
    }
1379
1380
    /**
1381
     * Generate an array of possible categories where this category can be moved to.
1382
     * Notice: its own parent will be included in the list: it's up to the frontend
1383
     * to disable this element.
1384
     *
1385
     * @return array 2-dimensional array - every element contains 3 subelements (id, name, level)
1386
     */
1387
    public function get_target_categories()
1388
    {
1389
        // the root or a course -> not movable
1390
        if (!$this->is_movable()) {
1391
            return null;
1392
        } else {
1393
            // otherwise:
1394
            // - course independent category
1395
            //   -> movable to root or other independent categories
1396
            // - category inside a course
1397
            //   -> movable to root, independent categories or categories inside the course
1398
            $user = api_is_platform_admin() ? null : api_get_user_id();
1399
            $targets = [];
1400
            $level = 0;
1401
1402
            $root = [0, get_lang('Main folder'), $level];
1403
            $targets[] = $root;
1404
1405
            if (isset($this->course_code) && !empty($this->course_code)) {
1406
                $crscats = self::load(null, null, $this->course_code, 0);
1407
                foreach ($crscats as $cat) {
1408
                    if ($this->can_be_moved_to_cat($cat)) {
1409
                        $targets[] = [
1410
                            $cat->get_id(),
1411
                            $cat->get_name(),
1412
                            $level + 1,
1413
                        ];
1414
                        $targets = $this->addTargetSubcategories(
1415
                            $targets,
1416
                            $level + 1,
1417
                            $cat->get_id()
1418
                        );
1419
                    }
1420
                }
1421
            }
1422
1423
            $indcats = self::load(null, $user, 0, 0);
1424
            foreach ($indcats as $cat) {
1425
                if ($this->can_be_moved_to_cat($cat)) {
1426
                    $targets[] = [$cat->get_id(), $cat->get_name(), $level + 1];
1427
                    $targets = $this->addTargetSubcategories(
1428
                        $targets,
1429
                        $level + 1,
1430
                        $cat->get_id()
1431
                    );
1432
                }
1433
            }
1434
1435
            return $targets;
1436
        }
1437
    }
1438
1439
    /**
1440
     * Move this category to the given category.
1441
     * If this category moves from inside a course to outside,
1442
     * its course code must be changed, as well as the course code
1443
     * of all underlying categories and evaluations. All links will
1444
     * be deleted as well !
1445
     */
1446
    public function move_to_cat($cat)
1447
    {
1448
        $this->set_parent_id($cat->get_id());
1449
        if ($this->get_course_code() != $cat->get_course_code()) {
1450
            $this->set_course_code($cat->get_course_code());
1451
            $this->applyCourseCodeToChildren();
1452
        }
1453
        $this->save();
1454
    }
1455
1456
    /**
1457
     * Generate an array of all categories the user can navigate to.
1458
     */
1459
    public function get_tree()
1460
    {
1461
        $targets = [];
1462
        $level = 0;
1463
        $root = [0, get_lang('Main folder'), $level];
1464
        $targets[] = $root;
1465
1466
        // course or platform admin
1467
        if (api_is_allowed_to_edit()) {
1468
            $user = api_is_platform_admin() ? null : api_get_user_id();
1469
            $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

1469
            /** @scrutinizer ignore-call */ 
1470
            $cats = self::get_root_categories_for_teacher($user);
Loading history...
1470
            foreach ($cats as $cat) {
1471
                $targets[] = [
1472
                    $cat->get_id(),
1473
                    $cat->get_name(),
1474
                    $level + 1,
1475
                ];
1476
                $targets = $this->add_subtree(
1477
                    $targets,
1478
                    $level + 1,
1479
                    $cat->get_id(),
1480
                    null
1481
                );
1482
            }
1483
        } else {
1484
            // student
1485
            $cats = $this->get_root_categories_for_student(api_get_user_id());
1486
            foreach ($cats as $cat) {
1487
                $targets[] = [
1488
                    $cat->get_id(),
1489
                    $cat->get_name(),
1490
                    $level + 1,
1491
                ];
1492
                $targets = $this->add_subtree(
1493
                    $targets,
1494
                    $level + 1,
1495
                    $cat->get_id(),
1496
                    1
1497
                );
1498
            }
1499
        }
1500
1501
        return $targets;
1502
    }
1503
1504
    /**
1505
     * Generate an array of courses that a teacher hasn't created a category for.
1506
     *
1507
     * @param int $user_id
1508
     *
1509
     * @return array 2-dimensional array - every element contains 2 subelements (code, title)
1510
     */
1511
    public function get_not_created_course_categories($user_id)
1512
    {
1513
        $tbl_main_courses = Database::get_main_table(TABLE_MAIN_COURSE);
1514
        $tbl_main_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1515
        $tbl_grade_categories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1516
1517
        $user_id = (int) $user_id;
1518
1519
        $sql = 'SELECT DISTINCT(code), title
1520
                FROM '.$tbl_main_courses.' cc, '.$tbl_main_course_user.' cu
1521
                WHERE 
1522
                    cc.id = cu.c_id AND 
1523
                    cu.status = '.COURSEMANAGER;
1524
1525
        if (!api_is_platform_admin()) {
1526
            $sql .= ' AND cu.user_id = '.$user_id;
1527
        }
1528
        $sql .= ' AND cc.code NOT IN
1529
             (
1530
                SELECT course_code FROM '.$tbl_grade_categories.'
1531
                WHERE
1532
                    parent_id = 0 AND
1533
                    course_code IS NOT NULL
1534
                )';
1535
        $result = Database::query($sql);
1536
1537
        $cats = [];
1538
        while ($data = Database::fetch_array($result)) {
1539
            $cats[] = [$data['code'], $data['title']];
1540
        }
1541
1542
        return $cats;
1543
    }
1544
1545
    /**
1546
     * Generate an array of all courses that a teacher is admin of.
1547
     *
1548
     * @param int $user_id
1549
     *
1550
     * @return array 2-dimensional array - every element contains 2 subelements (code, title)
1551
     */
1552
    public function get_all_courses($user_id)
1553
    {
1554
        $tbl_main_courses = Database::get_main_table(TABLE_MAIN_COURSE);
1555
        $tbl_main_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1556
        $sql = 'SELECT DISTINCT(code), title
1557
                FROM '.$tbl_main_courses.' cc, '.$tbl_main_course_user.' cu
1558
                WHERE cc.id = cu.c_id AND cu.status = '.COURSEMANAGER;
1559
        if (!api_is_platform_admin()) {
1560
            $sql .= ' AND cu.user_id = '.intval($user_id);
1561
        }
1562
1563
        $result = Database::query($sql);
1564
        $cats = [];
1565
        while ($data = Database::fetch_array($result)) {
1566
            $cats[] = [$data['code'], $data['title']];
1567
        }
1568
1569
        return $cats;
1570
    }
1571
1572
    /**
1573
     * Apply the same visibility to every subcategory, evaluation and link.
1574
     */
1575
    public function apply_visibility_to_children()
1576
    {
1577
        $cats = self::load(null, null, null, $this->id, null);
1578
        $evals = Evaluation::load(null, null, null, $this->id, null);
1579
        $links = LinkFactory::load(
1580
            null,
1581
            null,
1582
            null,
1583
            null,
1584
            null,
1585
            $this->id,
1586
            null
1587
        );
1588
        if (!empty($cats)) {
1589
            foreach ($cats as $cat) {
1590
                $cat->set_visible($this->is_visible());
1591
                $cat->save();
1592
                $cat->apply_visibility_to_children();
1593
            }
1594
        }
1595
        if (!empty($evals)) {
1596
            foreach ($evals as $eval) {
1597
                $eval->set_visible($this->is_visible());
1598
                $eval->save();
1599
            }
1600
        }
1601
        if (!empty($links)) {
1602
            foreach ($links as $link) {
1603
                $link->set_visible($this->is_visible());
1604
                $link->save();
1605
            }
1606
        }
1607
    }
1608
1609
    /**
1610
     * Check if a category contains evaluations with a result for a given student.
1611
     *
1612
     * @param int $studentId
1613
     *
1614
     * @return bool
1615
     */
1616
    public function hasEvaluationsWithStudentResults($studentId)
1617
    {
1618
        $evals = Evaluation::get_evaluations_with_result_for_student(
1619
            $this->id,
1620
            $studentId
1621
        );
1622
        if (0 != count($evals)) {
1623
            return true;
1624
        } else {
1625
            $cats = self::load(
1626
                null,
1627
                null,
1628
                null,
1629
                $this->id,
1630
                api_is_allowed_to_edit() ? null : 1
0 ignored issues
show
Bug introduced by
It seems like api_is_allowed_to_edit() ? null : 1 can also be of type integer; however, parameter $visible of Category::load() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

1630
                /** @scrutinizer ignore-type */ api_is_allowed_to_edit() ? null : 1
Loading history...
1631
            );
1632
1633
            /** @var Category $cat */
1634
            foreach ($cats as $cat) {
1635
                if ($cat->hasEvaluationsWithStudentResults($studentId)) {
1636
                    return true;
1637
                }
1638
            }
1639
1640
            return false;
1641
        }
1642
    }
1643
1644
    /**
1645
     * Retrieve all categories inside a course independent category
1646
     * that should be visible to a student.
1647
     *
1648
     * @param int   $categoryId parent category
1649
     * @param int   $studentId
1650
     * @param array $cats       optional: if defined, the categories will be added to this array
1651
     *
1652
     * @return array
1653
     */
1654
    public function getIndependentCategoriesWithStudentResult(
1655
        $categoryId,
1656
        $studentId,
1657
        $cats = []
1658
    ) {
1659
        $creator = api_is_allowed_to_edit() && !api_is_platform_admin() ? api_get_user_id() : null;
1660
1661
        $categories = self::load(
1662
            null,
1663
            $creator,
1664
            '0',
1665
            $categoryId,
1666
            api_is_allowed_to_edit() ? null : 1
0 ignored issues
show
Bug introduced by
It seems like api_is_allowed_to_edit() ? null : 1 can also be of type integer; however, parameter $visible of Category::load() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

1666
            /** @scrutinizer ignore-type */ api_is_allowed_to_edit() ? null : 1
Loading history...
1667
        );
1668
1669
        if (!empty($categories)) {
1670
            /** @var Category $category */
1671
            foreach ($categories as $category) {
1672
                if ($category->hasEvaluationsWithStudentResults($studentId)) {
1673
                    $cats[] = $category;
1674
                }
1675
            }
1676
        }
1677
1678
        return $cats;
1679
    }
1680
1681
    /**
1682
     * Return the session id (in any case, even if it's null or 0).
1683
     *
1684
     * @return int Session id (can be null)
1685
     */
1686
    public function get_session_id()
1687
    {
1688
        return $this->session_id;
1689
    }
1690
1691
    /**
1692
     * Get appropriate subcategories visible for the user (and optionally the course and session).
1693
     *
1694
     * @param int    $studentId   student id (default: all students)
1695
     * @param string $course_code Course code (optional)
1696
     * @param int    $session_id  Session ID (optional)
1697
     * @param bool   $order
1698
     *
1699
     * @return array Array of subcategories
1700
     */
1701
    public function get_subcategories(
1702
        $studentId = null,
1703
        $course_code = null,
1704
        $session_id = null,
1705
        $order = null
1706
    ) {
1707
        // 1 student
1708
        if (isset($studentId)) {
1709
            // Special case: this is the root
1710
            if (0 == $this->id) {
1711
                return $this->get_root_categories_for_student($studentId, $course_code, $session_id);
1712
            } else {
1713
                return self::load(
1714
                    null,
1715
                    null,
1716
                    $course_code,
1717
                    $this->id,
1718
                    api_is_allowed_to_edit() ? null : 1,
0 ignored issues
show
Bug introduced by
It seems like api_is_allowed_to_edit() ? null : 1 can also be of type integer; however, parameter $visible of Category::load() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

1718
                    /** @scrutinizer ignore-type */ api_is_allowed_to_edit() ? null : 1,
Loading history...
1719
                    $session_id,
1720
                    $order
1721
                );
1722
            }
1723
        } else {
1724
            // All students
1725
            // Course admin
1726
            if (api_is_allowed_to_edit() && !api_is_platform_admin()) {
1727
                // root
1728
                if (0 == $this->id) {
1729
                    // inside a course
1730
                    return $this->get_root_categories_for_teacher(
1731
                        api_get_user_id(),
1732
                        $course_code,
1733
                        $session_id,
1734
                        false
1735
                    );
1736
                } elseif (!empty($this->course_code)) {
1737
                    return self::load(
1738
                        null,
1739
                        null,
1740
                        $this->course_code,
1741
                        $this->id,
1742
                        null,
1743
                        $session_id,
1744
                        $order
1745
                    );
1746
                } elseif (!empty($course_code)) {
1747
                    // course independent
1748
                    return self::load(
1749
                        null,
1750
                        null,
1751
                        $course_code,
1752
                        $this->id,
1753
                        null,
1754
                        $session_id,
1755
                        $order
1756
                    );
1757
                } else {
1758
                    return self::load(
1759
                        null,
1760
                        api_get_user_id(),
1761
                        0,
1762
                        $this->id,
1763
                        null
1764
                    );
1765
                }
1766
            } elseif (api_is_platform_admin()) {
1767
                // platform admin
1768
                // we explicitly avoid listing subcats from another session
1769
                return self::load(
1770
                    null,
1771
                    null,
1772
                    $course_code,
1773
                    $this->id,
1774
                    null,
1775
                    $session_id,
1776
                    $order
1777
                );
1778
            }
1779
        }
1780
1781
        return [];
1782
    }
1783
1784
    /**
1785
     * Get appropriate evaluations visible for the user.
1786
     *
1787
     * @param int    $studentId   student id (default: all students)
1788
     * @param bool   $recursive   process subcategories (default: no recursion)
1789
     * @param string $course_code
1790
     * @param int    $sessionId
1791
     *
1792
     * @return array
1793
     */
1794
    public function get_evaluations(
1795
        $studentId = null,
1796
        $recursive = false,
1797
        $course_code = '',
1798
        $sessionId = 0
1799
    ) {
1800
        $evals = [];
1801
        $course_code = empty($course_code) ? $this->get_course_code() : $course_code;
1802
        $sessionId = empty($sessionId) ? $this->get_session_id() : $sessionId;
1803
1804
        // 1 student
1805
        if (isset($studentId) && !empty($studentId)) {
1806
            // Special case: this is the root
1807
            if (0 == $this->id) {
1808
                $evals = Evaluation::get_evaluations_with_result_for_student(
1809
                    0,
1810
                    $studentId
1811
                );
1812
            } else {
1813
                $evals = Evaluation::load(
1814
                    null,
1815
                    null,
1816
                    $course_code,
1817
                    $this->id,
1818
                    api_is_allowed_to_edit() ? null : 1
1819
                );
1820
            }
1821
        } else {
1822
            // All students
1823
            // course admin
1824
            if ((api_is_allowed_to_edit() || api_is_drh() || api_is_session_admin()) &&
1825
                !api_is_platform_admin()
1826
            ) {
1827
                // root
1828
                if (0 == $this->id) {
1829
                    $evals = Evaluation::load(
1830
                        null,
1831
                        api_get_user_id(),
1832
                        null,
1833
                        $this->id,
1834
                        null
1835
                    );
1836
                } elseif (isset($this->course_code) &&
1837
                    !empty($this->course_code)
1838
                ) {
1839
                    // inside a course
1840
                    $evals = Evaluation::load(
1841
                        null,
1842
                        null,
1843
                        $course_code,
1844
                        $this->id,
1845
                        null
1846
                    );
1847
                } else {
1848
                    // course independent
1849
                    $evals = Evaluation::load(
1850
                        null,
1851
                        api_get_user_id(),
1852
                        null,
1853
                        $this->id,
1854
                        null
1855
                    );
1856
                }
1857
            } else {
1858
                $evals = Evaluation::load(
1859
                    null,
1860
                    null,
1861
                    $course_code,
1862
                    $this->id,
1863
                    null
1864
                );
1865
            }
1866
        }
1867
1868
        if ($recursive) {
1869
            $subcats = $this->get_subcategories(
1870
                $studentId,
1871
                $course_code,
1872
                $sessionId
1873
            );
1874
1875
            if (!empty($subcats)) {
1876
                foreach ($subcats as $subcat) {
1877
                    $subevals = $subcat->get_evaluations(
1878
                        $studentId,
1879
                        true,
1880
                        $course_code
1881
                    );
1882
                    $evals = array_merge($evals, $subevals);
1883
                }
1884
            }
1885
        }
1886
1887
        return $evals;
1888
    }
1889
1890
    /**
1891
     * Get appropriate links visible for the user.
1892
     *
1893
     * @param int    $studentId   student id (default: all students)
1894
     * @param bool   $recursive   process subcategories (default: no recursion)
1895
     * @param string $course_code
1896
     * @param int    $sessionId
1897
     *
1898
     * @return array
1899
     */
1900
    public function get_links(
1901
        $studentId = null,
1902
        $recursive = false,
1903
        $course_code = '',
1904
        $sessionId = 0
1905
    ) {
1906
        $links = [];
1907
        $course_code = empty($course_code) ? $this->get_course_code() : $course_code;
1908
        $sessionId = empty($sessionId) ? $this->get_session_id() : $sessionId;
1909
1910
        // no links in root or course independent categories
1911
        if (0 == $this->id) {
1912
        } elseif (isset($studentId)) {
1913
            // 1 student $studentId
1914
            $links = LinkFactory::load(
1915
                null,
1916
                null,
1917
                null,
1918
                null,
1919
                empty($this->course_code) ? null : $course_code,
1920
                $this->id,
1921
                api_is_allowed_to_edit() ? null : 1
1922
            );
1923
        } else {
1924
            // All students -> only for course/platform admin
1925
            $links = LinkFactory::load(
1926
                null,
1927
                null,
1928
                null,
1929
                null,
1930
                empty($this->course_code) ? null : $this->course_code,
1931
                $this->id,
1932
                null
1933
            );
1934
        }
1935
1936
        if ($recursive) {
1937
            $subcats = $this->get_subcategories(
1938
                $studentId,
1939
                $course_code,
1940
                $sessionId
1941
            );
1942
            if (!empty($subcats)) {
1943
                /** @var Category $subcat */
1944
                foreach ($subcats as $subcat) {
1945
                    $sublinks = $subcat->get_links(
1946
                        $studentId,
1947
                        false,
1948
                        $course_code,
1949
                        $sessionId
1950
                    );
1951
                    $links = array_merge($links, $sublinks);
1952
                }
1953
            }
1954
        }
1955
1956
        return $links;
1957
    }
1958
1959
    /**
1960
     * Get all the categories from with the same given direct parent.
1961
     *
1962
     * @param int $catId Category parent ID
1963
     *
1964
     * @return array Array of Category objects
1965
     */
1966
    public function getCategories($catId)
1967
    {
1968
        $catId = (int) $catId;
1969
        $tblGradeCategories = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
1970
        $sql = 'SELECT * FROM '.$tblGradeCategories.'
1971
                WHERE parent_id = '.$catId;
1972
1973
        $result = Database::query($sql);
1974
        $categories = self::create_category_objects_from_sql_result($result);
1975
1976
        return $categories;
1977
    }
1978
1979
    /**
1980
     * Gets the type for the current object.
1981
     *
1982
     * @return string 'C' to represent "Category" object type
1983
     */
1984
    public function get_item_type()
1985
    {
1986
        return 'C';
1987
    }
1988
1989
    /**
1990
     * @param array $skills
1991
     */
1992
    public function set_skills($skills)
1993
    {
1994
        $this->skills = $skills;
1995
    }
1996
1997
    public function get_date()
1998
    {
1999
        return null;
2000
    }
2001
2002
    /**
2003
     * @return string
2004
     */
2005
    public function get_icon_name()
2006
    {
2007
        return 'cat';
2008
    }
2009
2010
    /**
2011
     * Find category by name.
2012
     *
2013
     * @param string $name_mask search string
2014
     *
2015
     * @return array category objects matching the search criterium
2016
     */
2017
    public function find_category($name_mask, $allcat)
2018
    {
2019
        $categories = [];
2020
        foreach ($allcat as $search_cat) {
2021
            if (!(false === strpos(strtolower($search_cat->get_name()), strtolower($name_mask)))) {
2022
                $categories[] = $search_cat;
2023
            }
2024
        }
2025
2026
        return $categories;
2027
    }
2028
2029
    /**
2030
     * This function, locks a category , only one who can unlock it is
2031
     * the platform administrator.
2032
     *
2033
     * @param int locked 1 or unlocked 0
2034
2035
     *
2036
     * @return bool|null
2037
     * */
2038
    public function lock($locked)
2039
    {
2040
        $table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
2041
        $sql = "UPDATE $table SET locked = '".intval($locked)."'
2042
                WHERE id='".intval($this->id)."'";
2043
        Database::query($sql);
2044
    }
2045
2046
    /**
2047
     * @param $locked
2048
     */
2049
    public function lockAllItems($locked)
2050
    {
2051
        if ('true' == api_get_setting('gradebook_locking_enabled')) {
2052
            $this->lock($locked);
2053
            $evals_to_lock = $this->get_evaluations();
2054
            if (!empty($evals_to_lock)) {
2055
                foreach ($evals_to_lock as $item) {
2056
                    $item->lock($locked);
2057
                }
2058
            }
2059
2060
            $link_to_lock = $this->get_links();
2061
            if (!empty($link_to_lock)) {
2062
                foreach ($link_to_lock as $item) {
2063
                    $item->lock($locked);
2064
                }
2065
            }
2066
2067
            $event_type = LOG_GRADEBOOK_UNLOCKED;
2068
            if (1 == $locked) {
2069
                $event_type = LOG_GRADEBOOK_LOCKED;
2070
            }
2071
            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

2071
            Event::/** @scrutinizer ignore-call */ 
2072
                   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...
2072
        }
2073
    }
2074
2075
    /**
2076
     * Generates a certificate for this user if everything matches.
2077
     *
2078
     * @param int  $category_id      gradebook id
2079
     * @param int  $user_id
2080
     * @param bool $sendNotification
2081
     *
2082
     * @return array
2083
     */
2084
    public static function generateUserCertificate(
2085
        $category_id,
2086
        $user_id,
2087
        $sendNotification = false
2088
    ) {
2089
        $user_id = (int) $user_id;
2090
        $category_id = (int) $category_id;
2091
2092
        // Generating the total score for a course
2093
        $category = self::load(
2094
            $category_id,
2095
            null,
2096
            null,
2097
            null,
2098
            null,
2099
            null,
2100
            false
2101
        );
2102
2103
        /** @var Category $category */
2104
        $category = $category[0];
2105
2106
        if (empty($category)) {
2107
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
2108
        }
2109
2110
        $sessionId = $category->get_session_id();
2111
        $courseCode = $category->get_course_code();
2112
        $courseInfo = api_get_course_info($courseCode);
2113
        $courseId = $courseInfo['real_id'];
2114
2115
        $userFinishedCourse = self::userFinishedCourse(
2116
            $user_id,
2117
            $category,
2118
            true
2119
        );
2120
2121
        if (!$userFinishedCourse) {
2122
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
2123
        }
2124
2125
        $skillToolEnabled = Skill::hasAccessToUserSkill(
2126
            api_get_user_id(),
2127
            $user_id
2128
        );
2129
2130
        $userHasSkills = false;
2131
        if ($skillToolEnabled) {
2132
            $skill = new Skill();
2133
            $skill->addSkillToUser(
2134
                $user_id,
2135
                $category,
2136
                $courseId,
2137
                $sessionId
2138
            );
2139
2140
            $objSkillRelUser = new SkillRelUser();
2141
            $userSkills = $objSkillRelUser->getUserSkills(
2142
                $user_id,
2143
                $courseId,
2144
                $sessionId
2145
            );
2146
            $userHasSkills = !empty($userSkills);
2147
        }
2148
2149
        // Block certification links depending gradebook configuration (generate certifications)
2150
        if (empty($category->getGenerateCertificates())) {
2151
            if ($userHasSkills) {
2152
                return [
2153
                    'badge_link' => Display::toolbarButton(
2154
                        get_lang('Export badges'),
2155
                        api_get_path(WEB_CODE_PATH)."gradebook/get_badges.php?user=$user_id",
2156
                        'external-link'
2157
                    ),
2158
                ];
2159
            }
2160
2161
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
2162
        }
2163
2164
        $scoretotal = $category->calc_score($user_id);
2165
2166
        // Do not remove this the gradebook/lib/fe/gradebooktable.class.php
2167
        // file load this variable as a global
2168
        $scoredisplay = ScoreDisplay::instance();
2169
        $my_score_in_gradebook = $scoredisplay->display_score(
2170
            $scoretotal,
2171
            SCORE_SIMPLE
2172
        );
2173
2174
        $my_certificate = GradebookUtils::get_certificate_by_user_id(
2175
            $category_id,
2176
            $user_id
2177
        );
2178
2179
        if (empty($my_certificate)) {
2180
            GradebookUtils::registerUserInfoAboutCertificate(
2181
                $category_id,
2182
                $user_id,
2183
                $my_score_in_gradebook,
2184
                api_get_utc_datetime()
2185
            );
2186
            $my_certificate = GradebookUtils::get_certificate_by_user_id(
2187
                $category_id,
2188
                $user_id
2189
            );
2190
        }
2191
2192
        $html = [];
2193
        if (!empty($my_certificate)) {
2194
            $certificate_obj = new Certificate(
2195
                $my_certificate['id'],
2196
                0,
2197
                $sendNotification
2198
            );
2199
2200
            $fileWasGenerated = $certificate_obj->isHtmlFileGenerated();
2201
2202
            // Fix when using custom certificate BT#15937
2203
            if ('true' === api_get_plugin_setting('customcertificate', 'enable_plugin_customcertificate')) {
2204
                $infoCertificate = CustomCertificatePlugin::getCertificateData($my_certificate['id'], $user_id);
2205
                if (!empty($infoCertificate)) {
2206
                    $fileWasGenerated = true;
2207
                }
2208
            }
2209
2210
            if (!empty($fileWasGenerated)) {
2211
                $url = api_get_path(WEB_PATH).'certificates/index.php?id='.$my_certificate['id'].'&user_id='.$user_id;
2212
                $certificates = Display::toolbarButton(
2213
                    get_lang('Display certificate'),
2214
                    $url,
2215
                    'eye',
2216
                    'primary',
2217
                    ['target' => '_blank']
2218
                );
2219
2220
                $exportToPDF = Display::url(
2221
                    Display::return_icon(
2222
                        'pdf.png',
2223
                        get_lang('Export to PDF'),
2224
                        [],
2225
                        ICON_SIZE_MEDIUM
2226
                    ),
2227
                    "$url&action=export"
2228
                );
2229
2230
                $hideExportLink = api_get_setting('hide_certificate_export_link');
2231
                $hideExportLinkStudent = api_get_setting('hide_certificate_export_link_students');
2232
                if ('true' === $hideExportLink || (api_is_student() && 'true' === $hideExportLinkStudent)) {
2233
                    $exportToPDF = null;
2234
                }
2235
2236
                $html = [
2237
                    'certificate_link' => $certificates,
2238
                    'pdf_link' => $exportToPDF,
2239
                    'pdf_url' => "$url&action=export",
2240
                ];
2241
            }
2242
2243
            if ($skillToolEnabled && $userHasSkills) {
2244
                $html['badge_link'] = Display::toolbarButton(
2245
                    get_lang('Export badges'),
2246
                    api_get_path(WEB_CODE_PATH)."gradebook/get_badges.php?user=$user_id",
2247
                    'external-link'
2248
                );
2249
            }
2250
2251
            return $html;
2252
        }
2253
    }
2254
2255
    /**
2256
     * @param int   $catId
2257
     * @param array $userList
2258
     */
2259
    public static function generateCertificatesInUserList($catId, $userList)
2260
    {
2261
        if (!empty($userList)) {
2262
            foreach ($userList as $userInfo) {
2263
                self::generateUserCertificate($catId, $userInfo['user_id']);
2264
            }
2265
        }
2266
    }
2267
2268
    /**
2269
     * @param int   $catId
2270
     * @param array $userList
2271
     */
2272
    public static function exportAllCertificates(
2273
        $catId,
2274
        $userList = []
2275
    ) {
2276
        $orientation = api_get_configuration_value('certificate_pdf_orientation');
2277
2278
        $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...
2279
        if (!empty($orientation)) {
2280
            $params['orientation'] = $orientation;
2281
        }
2282
2283
        $params['left'] = 0;
2284
        $params['right'] = 0;
2285
        $params['top'] = 0;
2286
        $params['bottom'] = 0;
2287
        $page_format = 'landscape' == $params['orientation'] ? 'A4-L' : 'A4';
2288
        $pdf = new PDF($page_format, $params['orientation'], $params);
2289
2290
        $certificate_list = GradebookUtils::get_list_users_certificates($catId, $userList);
2291
        $certificate_path_list = [];
2292
2293
        if (!empty($certificate_list)) {
2294
            foreach ($certificate_list as $index => $value) {
2295
                $list_certificate = GradebookUtils::get_list_gradebook_certificates_by_user_id(
2296
                    $value['user_id'],
2297
                    $catId
2298
                );
2299
                foreach ($list_certificate as $value_certificate) {
2300
                    $certificate_obj = new Certificate($value_certificate['id']);
2301
                    $certificate_obj->generate(['hide_print_button' => true]);
2302
                    if ($certificate_obj->isHtmlFileGenerated()) {
2303
                        $certificate_path_list[] = $certificate_obj->html_file;
2304
                    }
2305
                }
2306
            }
2307
        }
2308
2309
        if (!empty($certificate_path_list)) {
2310
            // Print certificates (without the common header/footer/watermark
2311
            //  stuff) and return as one multiple-pages PDF
2312
            $pdf->html_to_pdf(
2313
                $certificate_path_list,
2314
                get_lang('Certificates'),
2315
                null,
2316
                false,
2317
                false
2318
            );
2319
        }
2320
    }
2321
2322
    /**
2323
     * @param int $catId
2324
     */
2325
    public static function deleteAllCertificates($catId)
2326
    {
2327
        $certificate_list = GradebookUtils::get_list_users_certificates($catId);
2328
        if (!empty($certificate_list)) {
2329
            foreach ($certificate_list as $index => $value) {
2330
                $list_certificate = GradebookUtils::get_list_gradebook_certificates_by_user_id(
2331
                    $value['user_id'],
2332
                    $catId
2333
                );
2334
                foreach ($list_certificate as $value_certificate) {
2335
                    $certificate_obj = new Certificate($value_certificate['id']);
2336
                    $certificate_obj->delete(true);
2337
                }
2338
            }
2339
        }
2340
    }
2341
2342
    /**
2343
     * Check whether a user has finished a course by its gradebook.
2344
     *
2345
     * @param int       $userId           The user ID
2346
     * @param \Category $category         Optional. The gradebook category.
2347
     *                                    To check by the gradebook category
2348
     * @param bool      $recalculateScore Whether recalculate the score
2349
     *
2350
     * @return bool
2351
     */
2352
    public static function userFinishedCourse(
2353
        $userId,
2354
        \Category $category,
2355
        $recalculateScore = false
2356
    ) {
2357
        if (empty($category)) {
2358
            return false;
2359
        }
2360
2361
        $currentScore = self::getCurrentScore(
2362
            $userId,
2363
            $category,
2364
            $recalculateScore
2365
        );
2366
2367
        $minCertificateScore = $category->getCertificateMinScore();
2368
2369
        return $currentScore >= $minCertificateScore;
2370
    }
2371
2372
    /**
2373
     * Get the current score (as percentage) on a gradebook category for a user.
2374
     *
2375
     * @param int      $userId      The user id
2376
     * @param Category $category    The gradebook category
2377
     * @param bool     $recalculate
2378
     *
2379
     * @return float The score
2380
     */
2381
    public static function getCurrentScore(
2382
        $userId,
2383
        $category,
2384
        $recalculate = false
2385
    ) {
2386
        if (empty($category)) {
2387
            return 0;
2388
        }
2389
2390
        if ($recalculate) {
2391
            return self::calculateCurrentScore(
2392
                $userId,
2393
                $category
2394
            );
2395
        }
2396
2397
        $resultData = Database::select(
2398
            '*',
2399
            Database::get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_LOG),
2400
            [
2401
                'where' => [
2402
                    'category_id = ? AND user_id = ?' => [$category->get_id(), $userId],
2403
                ],
2404
                'order' => 'registered_at DESC',
2405
                'limit' => '1',
2406
            ],
2407
            'first'
2408
        );
2409
2410
        if (empty($resultData)) {
2411
            return 0;
2412
        }
2413
2414
        return $resultData['score'];
2415
    }
2416
2417
    /**
2418
     * Register the current score for a user on a category gradebook.
2419
     *
2420
     * @param float $score      The achieved score
2421
     * @param int   $userId     The user id
2422
     * @param int   $categoryId The gradebook category
2423
     *
2424
     * @return int The insert id
2425
     */
2426
    public static function registerCurrentScore($score, $userId, $categoryId)
2427
    {
2428
        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...
2429
            Database::get_main_table(TABLE_MAIN_GRADEBOOK_SCORE_LOG),
2430
            [
2431
                'category_id' => intval($categoryId),
2432
                'user_id' => intval($userId),
2433
                'score' => api_float_val($score),
2434
                'registered_at' => api_get_utc_datetime(),
2435
            ]
2436
        );
2437
    }
2438
2439
    /**
2440
     * @return array
2441
     */
2442
    public function getStudentList()
2443
    {
2444
        return $this->studentList;
2445
    }
2446
2447
    /**
2448
     * @param array $list
2449
     */
2450
    public function setStudentList($list)
2451
    {
2452
        $this->studentList = $list;
2453
    }
2454
2455
    /**
2456
     * @return string
2457
     */
2458
    public static function getUrl()
2459
    {
2460
        $url = Session::read('gradebook_dest');
2461
        if (empty($url)) {
2462
            // We guess the link
2463
            $courseInfo = api_get_course_info();
2464
            if (!empty($courseInfo)) {
2465
                return api_get_path(WEB_CODE_PATH).'gradebook/index.php?'.api_get_cidreq().'&';
2466
            } else {
2467
                return api_get_path(WEB_CODE_PATH).'gradebook/gradebook.php?';
2468
            }
2469
        }
2470
2471
        return $url;
2472
    }
2473
2474
    /**
2475
     * Destination is index.php or gradebook.php.
2476
     *
2477
     * @param string $url
2478
     */
2479
    public static function setUrl($url)
2480
    {
2481
        switch ($url) {
2482
            case 'gradebook.php':
2483
                $url = api_get_path(WEB_CODE_PATH).'gradebook/gradebook.php?';
2484
                break;
2485
            case 'index.php':
2486
                $url = api_get_path(WEB_CODE_PATH).'gradebook/index.php?'.api_get_cidreq().'&';
2487
                break;
2488
        }
2489
        Session::write('gradebook_dest', $url);
2490
    }
2491
2492
    /**
2493
     * @return int
2494
     */
2495
    public function getGradeBooksToValidateInDependence()
2496
    {
2497
        return $this->gradeBooksToValidateInDependence;
2498
    }
2499
2500
    /**
2501
     * @param int $value
2502
     *
2503
     * @return Category
2504
     */
2505
    public function setGradeBooksToValidateInDependence($value)
2506
    {
2507
        $this->gradeBooksToValidateInDependence = $value;
2508
2509
        return $this;
2510
    }
2511
2512
    /**
2513
     * Return HTML code with links to download and view certificate.
2514
     *
2515
     * @return string
2516
     */
2517
    public static function getDownloadCertificateBlock(array $certificate)
2518
    {
2519
        if (!isset($certificate['pdf_url'])) {
2520
            return '';
2521
        }
2522
2523
        $downloadLink = Display::toolbarButton(
2524
            get_lang('Download certificate in PDF'),
2525
            $certificate['pdf_url'],
2526
            'file-pdf-o'
2527
        );
2528
        $viewLink = $certificate['certificate_link'];
2529
2530
        return "
2531
            <div class='panel panel-default'>
2532
                <div class='panel-body'>
2533
                    <h3 class='text-center'>".get_lang('You can now download your certificate by clicking here')."</h3>
2534
                    <div class='text-center'>$downloadLink $viewLink</div>
2535
                </div>
2536
            </div>
2537
        ";
2538
    }
2539
2540
    /**
2541
     * Find a gradebook category by the certificate ID.
2542
     *
2543
     * @param int $id certificate id
2544
     *
2545
     * @throws \Doctrine\ORM\NonUniqueResultException
2546
     *
2547
     * @return Category|null
2548
     */
2549
    public static function findByCertificate($id)
2550
    {
2551
        $category = Database::getManager()
2552
            ->createQuery('SELECT c.catId FROM ChamiloCoreBundle:GradebookCertificate c WHERE c.id = :id')
2553
            ->setParameters(['id' => $id])
2554
            ->getOneOrNullResult();
2555
2556
        if (empty($category)) {
2557
            return null;
2558
        }
2559
2560
        $category = self::load($category['catId']);
2561
2562
        if (empty($category)) {
2563
            return null;
2564
        }
2565
2566
        return $category[0];
2567
    }
2568
2569
    /**
2570
     * @param int $value
2571
     */
2572
    public function setDocumentId($value)
2573
    {
2574
        $this->documentId = (int) $value;
2575
    }
2576
2577
    /**
2578
     * @return int
2579
     */
2580
    public function getDocumentId()
2581
    {
2582
        return $this->documentId;
2583
    }
2584
2585
    /**
2586
     * Get the remaining weight in root category.
2587
     *
2588
     * @return int
2589
     */
2590
    public function getRemainingWeight()
2591
    {
2592
        $subCategories = $this->get_subcategories();
2593
2594
        $subWeight = 0;
2595
2596
        /** @var Category $subCategory */
2597
        foreach ($subCategories as $subCategory) {
2598
            $subWeight += $subCategory->get_weight();
2599
        }
2600
2601
        return $this->weight - $subWeight;
2602
    }
2603
2604
2605
    /**
2606
     * @return int
2607
     */
2608
    public function getCourseId()
2609
    {
2610
        return $this->courseId;
2611
    }
2612
2613
    /**
2614
     * @param int $courseId
2615
     *
2616
     * @return Category
2617
     */
2618
    public function setCourseId($courseId)
2619
    {
2620
        $this->courseId = $courseId;
2621
2622
        return $this;
2623
    }
2624
2625
    /**
2626
     * @return Category
2627
     */
2628
    private static function create_root_category()
2629
    {
2630
        $cat = new Category();
2631
        $cat->set_id(0);
2632
        $cat->set_name(get_lang('Main folder'));
2633
        $cat->set_description(null);
2634
        $cat->set_user_id(0);
2635
        $cat->set_course_code(null);
2636
        $cat->set_parent_id(null);
2637
        $cat->set_weight(0);
2638
        $cat->set_visible(1);
2639
        $cat->setGenerateCertificates(0);
2640
        $cat->setIsRequirement(false);
2641
2642
        return $cat;
2643
    }
2644
2645
    /**
2646
     * @param Doctrine\DBAL\Driver\Statement|null $result
2647
     *
2648
     * @return array
2649
     */
2650
    private static function create_category_objects_from_sql_result($result)
2651
    {
2652
        $categories = [];
2653
        $allow = api_get_configuration_value('allow_gradebook_stats');
2654
        if ($allow) {
2655
            $em = Database::getManager();
2656
            $repo = $em->getRepository('ChamiloCoreBundle:GradebookCategory');
2657
        }
2658
2659
        while ($data = Database::fetch_array($result)) {
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type null; however, parameter $result of Database::fetch_array() does only seem to accept Doctrine\DBAL\Driver\Statement, maybe add an additional type check? ( Ignorable by Annotation )

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

2659
        while ($data = Database::fetch_array(/** @scrutinizer ignore-type */ $result)) {
Loading history...
2660
            $cat = new Category();
2661
            $cat->set_id($data['id']);
2662
            $cat->set_name($data['name']);
2663
            $cat->set_description($data['description']);
2664
            $cat->set_user_id($data['user_id']);
2665
            $courseInfo = api_get_course_info_by_id($data['c_id']);
2666
            $cat->set_course_code($courseInfo['code']);
2667
            $cat->setCourseId($data['c_id']);
2668
            $cat->set_parent_id($data['parent_id']);
2669
            $cat->set_weight($data['weight']);
2670
            $cat->set_visible($data['visible']);
2671
            $cat->set_session_id($data['session_id']);
2672
            $cat->set_certificate_min_score($data['certif_min_score']);
2673
            $cat->set_grade_model_id($data['grade_model_id']);
2674
            $cat->set_locked($data['locked']);
2675
            $cat->setGenerateCertificates($data['generate_certificates']);
2676
            $cat->setIsRequirement($data['is_requirement']);
2677
            $cat->setCourseListDependency(isset($data['depends']) ? $data['depends'] : []);
2678
            $cat->setMinimumToValidate(isset($data['minimum_to_validate']) ? $data['minimum_to_validate'] : null);
2679
            $cat->setGradeBooksToValidateInDependence(isset($data['gradebooks_to_validate_in_dependence']) ? $data['gradebooks_to_validate_in_dependence'] : null);
2680
            $cat->setDocumentId($data['document_id']);
2681
            if ($allow) {
2682
                $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...
2683
            }
2684
2685
            $categories[] = $cat;
2686
        }
2687
2688
        return $categories;
2689
    }
2690
2691
    /**
2692
     * Internal function used by get_target_categories().
2693
     *
2694
     * @param array $targets
2695
     * @param int   $level
2696
     * @param int   $catid
2697
     *
2698
     * @return array
2699
     */
2700
    private function addTargetSubcategories($targets, $level, $catid)
2701
    {
2702
        $subcats = self::load(null, null, null, $catid);
2703
        foreach ($subcats as $cat) {
2704
            if ($this->can_be_moved_to_cat($cat)) {
2705
                $targets[] = [
2706
                    $cat->get_id(),
2707
                    $cat->get_name(),
2708
                    $level + 1,
2709
                ];
2710
                $targets = $this->addTargetSubcategories(
2711
                    $targets,
2712
                    $level + 1,
2713
                    $cat->get_id()
2714
                );
2715
            }
2716
        }
2717
2718
        return $targets;
2719
    }
2720
2721
    /**
2722
     * Internal function used by get_target_categories() and addTargetSubcategories()
2723
     * Can this category be moved to the given category ?
2724
     * Impossible when origin and target are the same... children won't be processed
2725
     * either. (a category can't be moved to one of its own children).
2726
     */
2727
    private function can_be_moved_to_cat($cat)
2728
    {
2729
        return $cat->get_id() != $this->get_id();
2730
    }
2731
2732
    /**
2733
     * Internal function used by move_to_cat().
2734
     */
2735
    private function applyCourseCodeToChildren()
2736
    {
2737
        $cats = self::load(null, null, null, $this->id, null);
2738
        $evals = Evaluation::load(null, null, null, $this->id, null);
2739
        $links = LinkFactory::load(
2740
            null,
2741
            null,
2742
            null,
2743
            null,
2744
            null,
2745
            $this->id,
2746
            null
2747
        );
2748
        /** @var Category $cat */
2749
        foreach ($cats as $cat) {
2750
            $cat->set_course_code($this->get_course_code());
2751
            $cat->save();
2752
            $cat->applyCourseCodeToChildren();
2753
        }
2754
2755
        foreach ($evals as $eval) {
2756
            $eval->set_course_code($this->get_course_code());
2757
            $eval->save();
2758
        }
2759
2760
        foreach ($links as $link) {
2761
            $link->delete();
2762
        }
2763
    }
2764
2765
    /**
2766
     * Internal function used by get_tree().
2767
     *
2768
     * @param int      $level
2769
     * @param int|null $visible
2770
     *
2771
     * @return array
2772
     */
2773
    private function add_subtree($targets, $level, $catid, $visible)
2774
    {
2775
        $subcats = self::load(null, null, null, $catid, $visible);
0 ignored issues
show
Bug introduced by
It seems like $visible can also be of type integer; however, parameter $visible of Category::load() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

2775
        $subcats = self::load(null, null, null, $catid, /** @scrutinizer ignore-type */ $visible);
Loading history...
2776
2777
        if (!empty($subcats)) {
2778
            foreach ($subcats as $cat) {
2779
                $targets[] = [
2780
                    $cat->get_id(),
2781
                    $cat->get_name(),
2782
                    $level + 1,
2783
                ];
2784
                $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

2784
                /** @scrutinizer ignore-call */ 
2785
                $targets = self::add_subtree(
Loading history...
2785
                    $targets,
2786
                    $level + 1,
2787
                    $cat->get_id(),
2788
                    $visible
2789
                );
2790
            }
2791
        }
2792
2793
        return $targets;
2794
    }
2795
2796
    /**
2797
     * Calculate the current score on a gradebook category for a user.
2798
     *
2799
     * @param int      $userId   The user id
2800
     * @param Category $category The gradebook category
2801
     *
2802
     * @return float The score
2803
     */
2804
    private static function calculateCurrentScore(
2805
        $userId,
2806
        $category
2807
    ) {
2808
        if (empty($category)) {
2809
            return 0;
2810
        }
2811
        $courseEvaluations = $category->get_evaluations(
2812
            $userId,
2813
            true
2814
        );
2815
        $courseLinks = $category->get_links($userId, true);
2816
        $evaluationsAndLinks = array_merge($courseEvaluations, $courseLinks);
2817
        $categoryScore = 0;
2818
        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...
2819
            $item = $evaluationsAndLinks[$i];
2820
            $score = $item->calc_score($userId);
2821
            $itemValue = 0;
2822
            if (!empty($score)) {
2823
                $divider = 0 == $score[1] ? 1 : $score[1];
2824
                $itemValue = $score[0] / $divider * $item->get_weight();
2825
            }
2826
2827
            $categoryScore += $itemValue;
2828
        }
2829
2830
        return api_float_val($categoryScore);
2831
    }
2832
}
2833