Completed
Push — master ( d5b39b...ee3fd0 )
by Julito
27:10
created

Skill::getDirectParents()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 5
nop 1
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\UserBundle\Entity\User;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, User. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
5
use Chamilo\UserBundle\Entity\Repository\UserRepository;
6
use Fhaculty\Graph\Vertex;
7
use Fhaculty\Graph\Graph;
8
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
9
use Chamilo\SkillBundle\Entity\SkillRelItem;
10
11
/**
12
 * Class SkillProfile
13
 * @package chamilo.library
14
 */
15
class SkillProfile extends Model
16
{
17
    public $columns = ['id', 'name', 'description'];
18
19
    /**
20
     * Constructor
21
     */
22
    public function __construct()
23
    {
24
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_PROFILE);
25
        $this->table_rel_profile = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
0 ignored issues
show
Bug Best Practice introduced by
The property table_rel_profile does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
26
    }
27
28
    /**
29
     * @return array
30
     */
31
    public function getProfiles()
32
    {
33
        $sql = "SELECT * FROM $this->table p
34
                INNER JOIN $this->table_rel_profile sp
35
                ON(p.id = sp.profile_id) ";
36
        $result = Database::query($sql);
37
        $profiles = Database::store_result($result, 'ASSOC');
38
39
        return $profiles;
40
    }
41
42
    /**
43
     * This function is for editing profile info from profile_id.
44
     * @param int    $profileId
45
     * @param string $name
46
     * @param string $description
47
     * @return bool
48
     */
49
    public function updateProfileInfo($profileId, $name, $description)
50
    {
51
        $profileId = (int) $profileId;
52
53
        if (empty($profileId)) {
54
            return false;
55
        }
56
57
        $name = Database::escape_string($name);
58
        $description = Database::escape_string($description);
59
60
        $sql = "UPDATE $this->table SET
61
                    name = '$name',
62
                    description = '$description'
63
                WHERE id = $profileId ";
64
        Database::query($sql);
65
66
        return true;
67
    }
68
69
    /**
70
     * Call the save method of the parent class and the SkillRelProfile object
71
     * @param array $params
72
     * @param bool $show_query Whether to show the query in parent save() method
73
     *
74
     * @return mixed Profile ID or false if incomplete params
75
     */
76
    public function save($params, $show_query = false)
77
    {
78
        if (!empty($params)) {
79
            $profile_id = parent::save($params, $show_query);
80
            if ($profile_id) {
81
                $skill_rel_profile = new SkillRelProfile();
82
                if (isset($params['skills'])) {
83
                    foreach ($params['skills'] as $skill_id) {
84
                        $attributes = [
85
                            'skill_id' => $skill_id,
86
                            'profile_id' => $profile_id,
87
                        ];
88
                        $skill_rel_profile->save($attributes);
89
                    }
90
                }
91
                return $profile_id;
92
            }
93
        }
94
95
        return false;
96
    }
97
98
    /**
99
     * Delete a skill profile
100
     * @param int $id The skill profile id
101
     * @return boolean Whether delete a skill profile
102
     */
103
    public function delete($id)
104
    {
105
        Database::delete(
106
            $this->table_rel_profile,
107
            [
108
                'profile_id' => $id,
109
            ]
110
        );
111
112
        return parent::delete($id);
113
    }
114
}
115
116
/**
117
 * Class SkillRelProfile
118
 */
119
class SkillRelProfile extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
120
{
121
    public $columns = ['id', 'skill_id', 'profile_id'];
122
123
    /**
124
     * Constructor
125
     */
126
    public function __construct()
127
    {
128
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
129
        $this->tableProfile = Database::get_main_table(TABLE_MAIN_SKILL_PROFILE);
0 ignored issues
show
Bug Best Practice introduced by
The property tableProfile does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
130
    }
131
132
    /**
133
     * @param int $profileId
134
     * @return array
135
     */
136
    public function getSkillsByProfile($profileId)
137
    {
138
        $profileId = intval($profileId);
139
        $skills = $this->get_all(['where' => ['profile_id = ? ' => $profileId]]);
140
        $return = [];
141
        if (!empty($skills)) {
142
            foreach ($skills as $skill_data) {
143
                $return[] = $skill_data['skill_id'];
144
            }
145
        }
146
147
        return $return;
148
    }
149
150
    /**
151
     * This function is for getting profile info from profile_id.
152
     * @param int $profileId
153
     * @return array
154
     */
155
    public function getProfileInfo($profileId)
156
    {
157
        $sql = "SELECT * FROM $this->table p
158
                INNER JOIN $this->tableProfile pr
159
                ON (pr.id = p.profile_id)
160
                WHERE p.profile_id = ".intval($profileId);
161
        $result = Database::query($sql);
162
        $profileData = Database::fetch_array($result, 'ASSOC');
163
164
        return $profileData;
165
    }
166
}
167
168
/**
169
 * Class SkillRelSkill
170
 */
171
class SkillRelSkill extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
172
{
173
    public $columns = ['skill_id', 'parent_id', 'relation_type', 'level'];
174
175
    /**
176
     * Constructor
177
     */
178
    public function __construct()
179
    {
180
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
181
        $this->tableSkill = Database::get_main_table(TABLE_MAIN_SKILL);
0 ignored issues
show
Bug Best Practice introduced by
The property tableSkill does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
182
    }
183
184
    /**
185
     * Gets an element
186
     * @param int $id
187
     * @return array
188
     */
189
    public function getSkillInfo($id)
190
    {
191
        if (empty($id)) {
192
            return [];
193
        }
194
        $result = Database::select(
195
            '*',
196
            $this->table,
197
            ['where' => ['skill_id = ?' => intval($id)]],
198
            'first'
199
        );
200
201
        return $result;
202
    }
203
204
    /**
205
     * @param int $skillId
206
     * @param bool $add_child_info
207
     * @return array
208
     */
209
    public function getSkillParents($skillId, $add_child_info = true)
210
    {
211
        $skillId = intval($skillId);
212
        $sql = 'SELECT child.* FROM '.$this->table.' child
213
                LEFT JOIN '.$this->table.' parent
214
                ON child.parent_id = parent.skill_id
215
                WHERE child.skill_id = '.$skillId.' ';
216
        $result = Database::query($sql);
217
        $skill = Database::store_result($result, 'ASSOC');
218
        $skill = isset($skill[0]) ? $skill[0] : null;
219
220
        $parents = [];
221
        if (!empty($skill)) {
222
            if ($skill['parent_id'] != null) {
223
                $parents = self::getSkillParents($skill['parent_id']);
0 ignored issues
show
Bug Best Practice introduced by
The method SkillRelSkill::getSkillParents() 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

223
                /** @scrutinizer ignore-call */ 
224
                $parents = self::getSkillParents($skill['parent_id']);
Loading history...
224
            }
225
            if ($add_child_info) {
226
                $parents[] = $skill;
227
            }
228
        }
229
        return $parents;
230
    }
231
232
    /**
233
     * @param int $skillId
234
     * @return array
235
     */
236
    public function getDirectParents($skillId)
237
    {
238
        $skillId = (int) $skillId;
239
        $sql = 'SELECT parent_id as skill_id 
240
                FROM '.$this->table.'
241
                WHERE skill_id = '.$skillId;
242
        $result = Database::query($sql);
243
        $skill = Database::store_result($result, 'ASSOC');
244
        $skill = isset($skill[0]) ? $skill[0] : null;
245
        $parents = [];
246
        if (!empty($skill)) {
247
            $parents[] = $skill;
248
        }
249
250
        return $parents;
251
    }
252
253
    /**
254
     * @param int $skill_id
255
     * @param bool $load_user_data
256
     * @param bool $user_id
257
     * @return array
258
     */
259
    public function getChildren(
260
        $skill_id,
261
        $load_user_data = false,
262
        $user_id = false,
263
        $order = ''
0 ignored issues
show
Unused Code introduced by
The parameter $order is not used and could be removed. ( Ignorable by Annotation )

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

263
        /** @scrutinizer ignore-unused */ $order = ''

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
264
    ) {
265
        $skill_id = (int) $skill_id;
266
        $sql = 'SELECT parent.* FROM '.$this->tableSkill.' skill
267
                INNER JOIN '.$this->table.' parent
268
                ON parent.id = skill.id
269
                WHERE parent_id = '.$skill_id.'
270
                ORDER BY skill.name ASC';
271
        $result = Database::query($sql);
272
        $skills = Database::store_result($result, 'ASSOC');
273
274
        $skill_obj = new Skill();
275
        $skill_rel_user = new SkillRelUser();
276
277
        if ($load_user_data) {
278
            $passed_skills = $skill_rel_user->getUserSkills($user_id);
279
            $done_skills = [];
280
            foreach ($passed_skills as $done_skill) {
281
                $done_skills[] = $done_skill['skill_id'];
282
            }
283
        }
284
285
        if (!empty($skills)) {
286
            foreach ($skills as &$skill) {
287
                $skill['data'] = $skill_obj->get($skill['skill_id']);
288
                if (isset($skill['data']) && !empty($skill['data'])) {
289
                    if (!empty($done_skills)) {
290
                        $skill['data']['passed'] = 0;
291
                        if (in_array($skill['skill_id'], $done_skills)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $done_skills does not seem to be defined for all execution paths leading up to this point.
Loading history...
292
                            $skill['data']['passed'] = 1;
293
                        }
294
                    }
295
                } else {
296
                    $skill = null;
297
                }
298
            }
299
        }
300
301
        return $skills;
302
    }
303
304
    /**
305
     * @param array $params
306
     * @return bool
307
     */
308
    public function updateBySkill($params)
309
    {
310
        $result = Database::update(
311
            $this->table,
312
            $params,
313
            ['skill_id = ? ' => $params['skill_id']]
314
        );
315
        if ($result) {
316
            return true;
317
        }
318
319
        return false;
320
    }
321
322
    /**
323
     * @param int $skill_id
324
     * @param int $parent_id
325
     * @return bool
326
     */
327
    public function relationExists($skill_id, $parent_id)
328
    {
329
        $result = $this->find(
330
            'all',
331
            [
332
                'where' => [
333
                    'skill_id = ? AND parent_id = ?' => [
334
                        $skill_id,
335
                        $parent_id,
336
                    ],
337
                ],
338
            ]
339
        );
340
341
        if (!empty($result)) {
342
            return true;
343
        }
344
        return false;
345
    }
346
}
347
348
/**
349
 * Class SkillRelGradebook
350
 */
351
class SkillRelGradebook extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
352
{
353
    public $columns = ['id', 'gradebook_id', 'skill_id'];
354
355
    /**
356
     * SkillRelGradebook constructor.
357
     */
358
    public function __construct()
359
    {
360
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
361
    }
362
363
    /**
364
     * @param int $gradebookId
365
     * @param int $skillId
366
     * @return bool
367
     */
368
    public function existsGradeBookSkill($gradebookId, $skillId)
369
    {
370
        $result = $this->find(
371
            'all',
372
            [
373
                'where' => [
374
                    'gradebook_id = ? AND skill_id = ?' => [
375
                        $gradebookId,
376
                        $skillId,
377
                    ],
378
                ],
379
            ]
380
        );
381
        if (!empty($result)) {
382
            return true;
383
        }
384
        return false;
385
    }
386
387
    /**
388
     * Gets an element
389
     */
390
    public function getSkillInfo($skill_id, $gradebookId)
391
    {
392
        if (empty($skill_id)) {
393
            return [];
394
        }
395
        $result = Database::select(
396
            '*',
397
            $this->table,
398
            [
399
                'where' => [
400
                    'skill_id = ? AND gradebook_id = ? ' => [
401
                        $skill_id,
402
                        $gradebookId,
403
                    ],
404
                ],
405
            ],
406
            'first'
407
        );
408
        return $result;
409
    }
410
411
    /**
412
     * @param int $skill_id
413
     * @param array $gradebook_list
414
     */
415
    public function updateGradeBookListBySkill($skill_id, $gradebook_list)
416
    {
417
        $original_gradebook_list = $this->find(
418
            'all',
419
            ['where' => ['skill_id = ?' => [$skill_id]]]
420
        );
421
        $gradebooks_to_remove = [];
422
        $gradebooks_to_add = [];
423
        $original_gradebook_list_ids = [];
424
425
        if (!empty($original_gradebook_list)) {
426
            foreach ($original_gradebook_list as $gradebook) {
427
                if (!in_array($gradebook['gradebook_id'], $gradebook_list)) {
428
                    $gradebooks_to_remove[] = $gradebook['id'];
429
                }
430
            }
431
            foreach ($original_gradebook_list as $gradebook_item) {
432
                $original_gradebook_list_ids[] = $gradebook_item['gradebook_id'];
433
            }
434
        }
435
436
        if (!empty($gradebook_list)) {
437
            foreach ($gradebook_list as $gradebook_id) {
438
                if (!in_array($gradebook_id, $original_gradebook_list_ids)) {
439
                    $gradebooks_to_add[] = $gradebook_id;
440
                }
441
            }
442
        }
443
444
        if (!empty($gradebooks_to_remove)) {
445
            foreach ($gradebooks_to_remove as $id) {
446
                $this->delete($id);
447
            }
448
        }
449
450
        if (!empty($gradebooks_to_add)) {
451
            foreach ($gradebooks_to_add as $gradebook_id) {
452
                $attributes = [
453
                    'skill_id' => $skill_id,
454
                    'gradebook_id' => $gradebook_id,
455
                ];
456
                $this->save($attributes);
457
            }
458
        }
459
    }
460
461
    /**
462
     * @param array $params
463
     * @return bool|void
464
     */
465
    public function updateBySkill($params)
466
    {
467
        $skillInfo = $this->existsGradeBookSkill(
468
            $params['gradebook_id'],
469
            $params['skill_id']
470
        );
471
472
        if ($skillInfo) {
473
            return;
474
        } else {
475
            $result = $this->save($params);
476
        }
477
        if ($result) {
478
            return true;
479
        }
480
        return false;
481
    }
482
}
483
484
/**
485
 * Class SkillRelUser
486
 */
487
class SkillRelUser extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
488
{
489
    public $columns = [
490
        'id',
491
        'user_id',
492
        'skill_id',
493
        'acquired_skill_at',
494
        'assigned_by',
495
        'course_id',
496
        'session_id',
497
    ];
498
499
    /**
500
     * Constructor
501
     */
502
    public function __construct()
503
    {
504
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
505
    }
506
507
    /**
508
     * @param array $skill_list
509
     * @return array
510
     */
511
    public function getUserBySkills($skill_list)
512
    {
513
        $users = [];
514
        if (!empty($skill_list)) {
515
            $skill_list = array_map('intval', $skill_list);
516
            $skill_list = implode("', '", $skill_list);
517
518
            $sql = "SELECT user_id FROM {$this->table}
519
                    WHERE skill_id IN ('$skill_list') ";
520
521
            $result = Database::query($sql);
522
            $users  = Database::store_result($result, 'ASSOC');
523
        }
524
        return $users;
525
    }
526
527
    /**
528
     * Get the achieved skills for the user
529
     * @param int $userId
530
     * @param int $courseId Optional. The course id
531
     * @param int $sessionId Optional. The session id
532
     * @return array The skill list. Otherwise return false
533
     */
534
    public function getUserSkills($userId, $courseId = 0, $sessionId = 0)
535
    {
536
        if (empty($userId)) {
537
            return [];
538
        }
539
540
        $courseId = intval($courseId);
541
        $sessionId = $sessionId ? intval($sessionId) : null;
542
        $whereConditions = [
543
            'user_id = ? ' => intval($userId),
544
        ];
545
546
        if ($courseId > 0) {
547
            $whereConditions['AND course_id = ? '] = $courseId;
548
            $whereConditions['AND session_id = ?'] = $sessionId;
549
        }
550
551
        $result = Database::select(
552
            'skill_id',
553
            $this->table,
554
            [
555
                'where' => $whereConditions,
556
            ],
557
            'all'
558
        );
559
        return $result;
560
    }
561
562
    /**
563
     * Get the relation data between user and skill
564
     * @param int $userId The user id
565
     * @param int $skillId The skill id
566
     * @param int $courseId The course id
567
     * @param int $sessionId Optional. The session id
568
     * @return array The relation data. Otherwise return false
569
     */
570
    public function getByUserAndSkill($userId, $skillId, $courseId, $sessionId = 0)
571
    {
572
        $where = [
573
            'user_id = ? AND skill_id = ? AND course_id = ? AND session_id = ?' => [
574
                intval($userId),
575
                intval($skillId),
576
                intval($courseId),
577
                $sessionId ? intval($sessionId) : null,
578
            ],
579
        ];
580
581
        return Database::select('*', $this->table, [
582
            'where' => $where,
583
        ], 'first');
584
    }
585
}
586
587
/**
588
 * Class Skill
589
 */
590
class Skill extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
591
{
592
    public $columns = [
593
        'id',
594
        'name',
595
        'description',
596
        'access_url_id',
597
        'short_code',
598
        'icon',
599
        'criteria',
600
    ];
601
    public $required = ['name'];
602
603
    /** Array of colours by depth, for the coffee wheel. Each depth has 4 col */
604
    /*var $colours = array(
605
      0 => array('#f9f0ab', '#ecc099', '#e098b0', '#ebe378'),
606
      1 => array('#d5dda1', '#4a5072', '#8dae43', '#72659d'),
607
      2 => array('#b28647', '#2e6093', '#393e64', '#1e8323'),
608
      3 => array('#9f6652', '#9f6652', '#9f6652', '#9f6652'),
609
      4 => array('#af643c', '#af643c', '#af643c', '#af643c'),
610
      5 => array('#72659d', '#72659d', '#72659d', '#72659d'),
611
      6 => array('#8a6e9e', '#8a6e9e', '#8a6e9e', '#8a6e9e'),
612
      7 => array('#92538c', '#92538c', '#92538c', '#92538c'),
613
      8 => array('#2e6093', '#2e6093', '#2e6093', '#2e6093'),
614
      9 => array('#3a5988', '#3a5988', '#3a5988', '#3a5988'),
615
     10 => array('#393e64', '#393e64', '#393e64', '#393e64'),
616
    );*/
617
    public function __construct()
618
    {
619
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL);
620
        $this->table_user = Database::get_main_table(TABLE_MAIN_USER);
0 ignored issues
show
Bug Best Practice introduced by
The property table_user does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
621
        $this->table_skill_rel_gradebook = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
0 ignored issues
show
Bug Best Practice introduced by
The property table_skill_rel_gradebook does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
622
        $this->table_skill_rel_user = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
0 ignored issues
show
Bug Best Practice introduced by
The property table_skill_rel_user does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
623
        $this->table_course = Database::get_main_table(TABLE_MAIN_COURSE);
0 ignored issues
show
Bug Best Practice introduced by
The property table_course does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
624
        $this->table_skill_rel_skill = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
0 ignored issues
show
Bug Best Practice introduced by
The property table_skill_rel_skill does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
625
        $this->table_gradebook = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
0 ignored issues
show
Bug Best Practice introduced by
The property table_gradebook does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
626
        $this->sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
0 ignored issues
show
Bug Best Practice introduced by
The property sessionTable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
627
    }
628
629
    /**
630
     * Gets an element
631
     * @param int $id
632
     *
633
     * @return array|mixed
634
     */
635
    public function get($id)
636
    {
637
        $result = parent::get($id);
638
        if (empty($result)) {
639
            return [];
640
        }
641
642
        $path = api_get_path(WEB_UPLOAD_PATH).'badges/';
643
        if (!empty($result['icon'])) {
644
            $iconSmall = sprintf(
645
                "%s-small.png",
646
                sha1($result['name'])
647
            );
648
649
            $iconBig = sprintf(
650
                "%s.png",
651
                sha1($result['name'])
652
            );
653
654
            $iconMini = $path.$iconSmall;
655
            $iconSmall = $path.$iconSmall;
656
            $iconBig = $path.$iconBig;
657
        } else {
658
            $iconMini = Display::returnIconPath('badges-default.png', ICON_SIZE_MEDIUM);
659
            $iconSmall = Display::returnIconPath('badges-default.png', ICON_SIZE_BIG);
660
            $iconBig = Display::returnIconPath('badges-default.png', ICON_SIZE_HUGE);
661
        }
662
663
        $result['icon_mini'] = $iconMini;
664
        $result['icon_small'] = $iconSmall;
665
        $result['icon_big'] = $iconBig;
666
667
        $result['img_mini'] = Display::img($iconBig, $result['name'], ['width' => ICON_SIZE_MEDIUM]);
668
        $result['img_big'] = Display::img($iconBig, $result['name']);
669
        $result['img_small'] = Display::img($iconSmall, $result['name']);
670
        $result['name'] = self::translateName($result['name']);
671
        $result['short_code'] = self::translateCode($result['short_code']);
672
673
        return $result;
674
    }
675
676
    /**
677
     * @param array $skills
678
     * @param string $imageSize mini|small|big
679
     * @param bool $addDivWrapper
680
     * @return string
681
     */
682
    public function processSkillList($skills, $imageSize = '', $addDivWrapper = true)
683
    {
684
        if (empty($skills)) {
685
            return '';
686
        }
687
688
        if (empty($imageSize)) {
689
            $imageSize = 'img_small';
690
        } else {
691
            $imageSize = "img_$imageSize";
692
        }
693
694
        $html = '';
695
        if ($addDivWrapper) {
696
            $html = '<div class="scrollbar-inner badges-sidebar">';
697
        }
698
        $html .= '<ul class="list-unstyled list-badges">';
699
        foreach ($skills as $skill) {
700
            if (isset($skill['data'])) {
701
                $skill = $skill['data'];
702
            }
703
            $html .= '<li class="thumbnail">';
704
            $item = $skill[$imageSize];
705
            $item .= '<div class="caption">
706
                        <p class="text-center">'.$skill['name'].'</p>
707
                      </div>';
708
            if (isset($skill['url'])) {
709
                $html .= Display::url($item, $skill['url'], ['target' => '_blank']);
710
            } else {
711
                $html .= $item;
712
            }
713
            $html .= '</li>';
714
        }
715
        $html .= '</ul>';
716
717
        if ($addDivWrapper) {
718
            $html .= '</div>';
719
        }
720
721
        return $html;
722
    }
723
724
    /**
725
     * @param $skills
726
     * @param string $imageSize mini|small|big
727
     * @param string $style
728
     * @param bool $showBadge
729
     * @param bool $showTitle
730
     *
731
     * @return string
732
     */
733
    public function processSkillListSimple($skills, $imageSize = '', $style = '', $showBadge = true, $showTitle = true)
734
    {
735
        if (empty($skills)) {
736
            return '';
737
        }
738
739
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
740
741
        if (empty($imageSize)) {
742
            $imageSize = 'img_small';
743
        } else {
744
            $imageSize = "img_$imageSize";
745
        }
746
747
        $html = '';
748
        foreach ($skills as $skill) {
749
            if (isset($skill['data'])) {
750
                $skill = $skill['data'];
751
            }
752
753
            $item = '';
754
            if ($showBadge) {
755
                $item = $skill[$imageSize];
756
            }
757
758
            $name = $skill['name'];
759
            if (!empty($skill['short_code'])) {
760
                $name = $skill['short_code'];
761
            }
762
763
            if (!$isHierarchicalTable) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
764
                //$item .= '<br />';
765
            }
766
767
            if ($showTitle) {
768
                $item .= $name;
769
            }
770
771
            if (isset($skill['url'])) {
772
                $html .= Display::url($item, $skill['url'], ['target' => '_blank', 'style' => $style]);
773
            } else {
774
                $html .= Display::url($item, '#', ['target' => '_blank', 'style' => $style]);
775
            }
776
        }
777
778
        return $html;
779
    }
780
781
    /**
782
     * @param int $id
783
     * @return array
784
     */
785
    public function getSkillInfo($id)
786
    {
787
        $skillRelSkill = new SkillRelSkill();
788
        $skillInfo = $this->get($id);
789
        if (!empty($skillInfo)) {
790
            $skillInfo['extra'] = $skillRelSkill->getSkillInfo($id);
791
            $skillInfo['gradebooks'] = self::getGradebooksBySkill($id);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::getGradebooksBySkill() 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

791
            /** @scrutinizer ignore-call */ 
792
            $skillInfo['gradebooks'] = self::getGradebooksBySkill($id);
Loading history...
792
        }
793
        return $skillInfo;
794
    }
795
796
    /**
797
     * @param array $skill_list
798
     * @return array
799
     */
800
    public function getSkillsInfo($skill_list)
801
    {
802
        $skill_list = array_map('intval', $skill_list);
803
        $skill_list = implode("', '", $skill_list);
804
805
        $sql = "SELECT * FROM {$this->table}
806
                WHERE id IN ('$skill_list') ";
807
808
        $result = Database::query($sql);
809
        $skills  = Database::store_result($result, 'ASSOC');
810
811
        foreach ($skills as &$skill) {
812
            if (!$skill['icon']) {
813
                continue;
814
            }
815
816
            $skill['icon_small'] = sprintf(
817
                "badges/%s-small.png",
818
                sha1($skill['name'])
819
            );
820
            $skill['name'] = self::translateName($skill['name']);
821
            $skill['short_code'] = self::translateCode($skill['short_code']);
822
        }
823
824
        return $skills;
825
    }
826
827
    /**
828
     * @param bool $load_user_data
829
     * @param bool $user_id
830
     * @param int $id
831
     * @param int $parent_id
832
     * @return array
833
     */
834
    public function get_all(
835
        $load_user_data = false,
836
        $user_id = false,
837
        $id = null,
838
        $parent_id = null
839
    ) {
840
        $id_condition = '';
841
        if (!empty($id)) {
842
            $id = intval($id);
843
            $id_condition = " WHERE s.id = $id";
844
        }
845
846
        if (!empty($parent_id)) {
847
            $parent_id = intval($parent_id);
848
            if (empty($id_condition)) {
849
                $id_condition = " WHERE ss.parent_id = $parent_id";
850
            } else {
851
                $id_condition = " AND ss.parent_id = $parent_id";
852
            }
853
        }
854
855
        $sql = "SELECT
856
                    s.id,
857
                    s.name,
858
                    s.description,
859
                    ss.parent_id,
860
                    ss.relation_type,
861
                    s.icon,
862
                    s.short_code,
863
                    s.status
864
                FROM {$this->table} s
865
                INNER JOIN {$this->table_skill_rel_skill} ss
866
                ON (s.id = ss.skill_id) $id_condition
867
                ORDER BY ss.id, ss.parent_id";
868
869
        $result = Database::query($sql);
870
        $skills = [];
871
        $webPath = api_get_path(WEB_UPLOAD_PATH);
872
        if (Database::num_rows($result)) {
873
            while ($row = Database::fetch_array($result, 'ASSOC')) {
874
                $skillInfo = self::get($row['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::get() 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

874
                /** @scrutinizer ignore-call */ 
875
                $skillInfo = self::get($row['id']);
Loading history...
875
876
                $row['img_mini'] = $skillInfo['img_mini'];
877
                $row['img_big'] = $skillInfo['img_big'];
878
                $row['img_small'] = $skillInfo['img_small'];
879
880
                $row['name'] = self::translateName($row['name']);
881
                $row['short_code'] = self::translateCode($row['short_code']);
882
                $skillRelSkill = new SkillRelSkill();
883
                $parents = $skillRelSkill->getSkillParents($row['id']);
884
                $row['level'] = count($parents) - 1;
885
                $row['gradebooks'] = self::getGradebooksBySkill($row['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::getGradebooksBySkill() 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

885
                /** @scrutinizer ignore-call */ 
886
                $row['gradebooks'] = self::getGradebooksBySkill($row['id']);
Loading history...
886
                $skills[$row['id']] = $row;
887
            }
888
        }
889
890
        // Load all children of the parent_id
891
        if (!empty($skills) && !empty($parent_id)) {
892
            foreach ($skills as $skill) {
893
                $children = self::get_all($load_user_data, $user_id, $id, $skill['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::get_all() 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

893
                /** @scrutinizer ignore-call */ 
894
                $children = self::get_all($load_user_data, $user_id, $id, $skill['id']);
Loading history...
894
                if (!empty($children)) {
895
                    //$skills = array_merge($skills, $children);
896
                    $skills = $skills + $children;
897
                }
898
            }
899
        }
900
        return $skills;
901
    }
902
903
    /**
904
     * @param int $skill_id
905
     * @return array|resource
906
     */
907
    public function getGradebooksBySkill($skill_id)
908
    {
909
        $skill_id = intval($skill_id);
910
        $sql = "SELECT g.* FROM {$this->table_gradebook} g
911
                INNER JOIN {$this->table_skill_rel_gradebook} sg
912
                ON g.id = sg.gradebook_id
913
                WHERE sg.skill_id = $skill_id";
914
        $result = Database::query($sql);
915
        $result = Database::store_result($result, 'ASSOC');
916
        return $result;
917
    }
918
919
    /**
920
     * Get one level children
921
     *
922
     * @param int $skill_id
923
     * @param bool $load_user_data
924
     * @return array
925
     */
926
    public function getChildren($skill_id, $load_user_data = false)
927
    {
928
        $skillRelSkill = new SkillRelSkill();
929
        if ($load_user_data) {
930
            $user_id = api_get_user_id();
931
            $skills = $skillRelSkill->getChildren($skill_id, true, $user_id);
932
        } else {
933
            $skills = $skillRelSkill->getChildren($skill_id);
934
        }
935
        return $skills;
936
    }
937
938
    /**
939
     * Get all children of the current node (recursive)
940
     * @param int $skillId
941
     * @return array
942
     */
943
    public function getAllChildren($skillId)
944
    {
945
        $skillRelSkill = new SkillRelSkill();
946
        $children = $skillRelSkill->getChildren($skillId);
947
        foreach ($children as $child) {
948
            $subChildren = $this->getAllChildren($child['id']);
949
        }
950
951
        if (!empty($subChildren)) {
952
            $children = array_merge($children, $subChildren);
953
        }
954
955
        return $children;
956
    }
957
958
    /**
959
     * Gets all parents from from the wanted skill
960
     */
961
    public function get_parents($skillId)
962
    {
963
        $skillRelSkill = new SkillRelSkill();
964
        $skills = $skillRelSkill->getSkillParents($skillId, true);
965
        foreach ($skills as &$skill) {
966
            $skill['data'] = $this->get($skill['skill_id']);
967
        }
968
        return $skills;
969
    }
970
971
    /**
972
     * All direct parents
973
     * @param int $skillId
974
     * @return array
975
     */
976
    public function getDirectParents($skillId)
977
    {
978
        $skillRelSkill = new SkillRelSkill();
979
        $skills = $skillRelSkill->getDirectParents($skillId, true);
980
        if (!empty($skills)) {
981
            foreach ($skills as &$skill) {
982
                $skillData = $this->get($skill['skill_id']);
983
                if (empty($skillData)) {
984
                    continue;
985
                }
986
                $skill['data'] = $skillData;
987
                $skill_info2 = $skillRelSkill->getSkillInfo($skill['skill_id']);
988
                $parentId = isset($skill_info2['parent_id']) ? isset($skill_info2['parent_id']) : 0;
989
                $skill['data']['parent_id'] = $parentId;
990
            }
991
992
            return $skills;
993
        }
994
995
        return [];
996
    }
997
998
    /**
999
     * Adds a new skill
1000
     * @param array $params
1001
     * @return bool|null
1002
     */
1003
    public function add($params)
1004
    {
1005
        if (!isset($params['parent_id'])) {
1006
            $params['parent_id'] = 1;
1007
        }
1008
1009
        if (!is_array($params['parent_id'])) {
1010
            $params['parent_id'] = [$params['parent_id']];
1011
        }
1012
1013
        $skillRelSkill = new SkillRelSkill();
1014
        $skillRelGradebook = new SkillRelGradebook();
1015
1016
        // Saving name, description
1017
        $skill_id = $this->save($params);
1018
        if ($skill_id) {
1019
            //Saving skill_rel_skill (parent_id, relation_type)
1020
            foreach ($params['parent_id'] as $parent_id) {
1021
                $relation_exists = $skillRelSkill->relationExists($skill_id, $parent_id);
1022
                if (!$relation_exists) {
1023
                    $attributes = [
1024
                        'skill_id' => $skill_id,
1025
                        'parent_id' => $parent_id,
1026
                        'relation_type' => (isset($params['relation_type']) ? $params['relation_type'] : 0),
1027
                        //'level'         => $params['level'],
1028
                    ];
1029
                    $skillRelSkill->save($attributes);
1030
                }
1031
            }
1032
1033
            if (!empty($params['gradebook_id'])) {
1034
                foreach ($params['gradebook_id'] as $gradebook_id) {
1035
                    $attributes = [];
1036
                    $attributes['gradebook_id'] = $gradebook_id;
1037
                    $attributes['skill_id'] = $skill_id;
1038
                    $skillRelGradebook->save($attributes);
1039
                }
1040
            }
1041
            return $skill_id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $skill_id returns the type integer which is incompatible with the documented return type null|boolean.
Loading history...
1042
        }
1043
        return null;
1044
    }
1045
1046
    /**
1047
     * @param int $user_id
1048
     * @param int $gradebookId
1049
     * @param int $courseId
1050
     * @param int $sessionId
1051
     */
1052
    public function addSkillToUser(
1053
        $user_id,
1054
        $gradebookId,
1055
        $courseId = 0,
1056
        $sessionId = 0
1057
    ) {
1058
        $skill_gradebook = new SkillRelGradebook();
1059
        $skill_rel_user = new SkillRelUser();
1060
1061
        $skill_gradebooks = $skill_gradebook->get_all(
1062
            ['where' => ['gradebook_id = ?' => $gradebookId]]
1063
        );
1064
        if (!empty($skill_gradebooks)) {
1065
            foreach ($skill_gradebooks as $skill_gradebook) {
1066
                $user_has_skill = $this->userHasSkill(
1067
                    $user_id,
1068
                    $skill_gradebook['skill_id'],
1069
                    $courseId,
1070
                    $sessionId
1071
                );
1072
                if (!$user_has_skill) {
1073
                    $params = [
1074
                        'user_id' => $user_id,
1075
                        'skill_id' => $skill_gradebook['skill_id'],
1076
                        'acquired_skill_at' => api_get_utc_datetime(),
1077
                        'course_id' => intval($courseId),
1078
                        'session_id' => $sessionId ? intval($sessionId) : null,
1079
                    ];
1080
1081
                    $skill_rel_user->save($params);
1082
                }
1083
            }
1084
        }
1085
    }
1086
1087
    /* Deletes a skill */
1088
    public function delete($skill_id)
1089
    {
1090
        /*$params = array('skill_id' => $skill_id);
1091
1092
        $skillRelSkill     = new SkillRelSkill();
1093
        $skills = $skillRelSkill->get_all(array('where'=>array('skill_id = ?' =>$skill_id)));
1094
1095
        $skill_rel_profile     = new SkillRelProfile();
1096
        $skillRelGradebook = new SkillRelGradebook();
1097
        $skill_rel_user     = new SkillRelUser();
1098
1099
        $this->delete($skill_id);
1100
1101
        $skillRelGradebook->delete($params);*/
1102
    }
1103
1104
    /**
1105
     * @param array $params
1106
     * @return null
1107
     */
1108
    public function edit($params)
1109
    {
1110
        if (!isset($params['parent_id'])) {
1111
            $params['parent_id'] = 1;
1112
        }
1113
1114
        $params['gradebook_id'] = isset($params['gradebook_id']) ? $params['gradebook_id'] : [];
1115
1116
        $skillRelSkill = new SkillRelSkill();
1117
        $skillRelGradebook = new SkillRelGradebook();
1118
1119
        // Saving name, description
1120
        $this->update($params);
1121
        $skillId = $params['id'];
1122
1123
        if ($skillId) {
1124
            // Saving skill_rel_skill (parent_id, relation_type)
1125
            if (!is_array($params['parent_id'])) {
1126
                $params['parent_id'] = [$params['parent_id']];
1127
            }
1128
1129
            // Cannot change parent of root
1130
            if ($skillId == 1) {
1131
                $params['parent_id'] = 0;
1132
            }
1133
1134
            foreach ($params['parent_id'] as $parent_id) {
1135
                $relation_exists = $skillRelSkill->relationExists($skillId, $parent_id);
1136
                if (!$relation_exists) {
1137
                    $attributes = [
1138
                        'skill_id' => $skillId,
1139
                        'parent_id' => $parent_id,
1140
                        'relation_type' => $params['relation_type'],
1141
                        //'level'         => $params['level'],
1142
                    ];
1143
                    $skillRelSkill->updateBySkill($attributes);
1144
                }
1145
            }
1146
1147
            $skillRelGradebook->updateGradeBookListBySkill(
1148
                $skillId,
1149
                $params['gradebook_id']
1150
            );
1151
            return $skillId;
1152
        }
1153
        return null;
1154
    }
1155
1156
    /**
1157
     * Get user's skills
1158
     *
1159
     * @param int $userId User's id
1160
     * @param bool $getSkillData
1161
     * @param int $courseId
1162
     * @param int $sessionId
1163
     *
1164
     * @return array
1165
     */
1166
    public function getUserSkills($userId, $getSkillData = false, $courseId = 0, $sessionId = 0)
1167
    {
1168
        $userId = (int) $userId;
1169
        $courseId = (int) $courseId;
1170
        $sessionId = (int) $sessionId;
1171
1172
        $courseCondition = '';
1173
        if (!empty($courseId)) {
1174
            $courseCondition = " AND course_id = $courseId ";
1175
        }
1176
1177
        $sessionCondition = '';
1178
        if (!empty($sessionId)) {
1179
            $sessionCondition = " AND course_id = $sessionId ";
1180
        }
1181
1182
        $sql = 'SELECT DISTINCT
1183
                    s.id,
1184
                    s.name,
1185
                    s.icon,
1186
                    u.id as issue,
1187
                    u.acquired_skill_at,
1188
                    u.course_id
1189
                FROM '.$this->table_skill_rel_user.' u
1190
                INNER JOIN '.$this->table.' s
1191
                ON u.skill_id = s.id
1192
                WHERE
1193
                    user_id = '.$userId.' '.$sessionCondition.' '.$courseCondition;
1194
1195
        $result = Database::query($sql);
1196
        $skills = Database::store_result($result, 'ASSOC');
1197
        $skillList = [];
1198
        if (!empty($skills)) {
1199
            foreach ($skills as $skill) {
1200
                if ($getSkillData) {
1201
                    $skillData = $this->get($skill['id']);
1202
                    $skillData['url'] = api_get_path(WEB_PATH).'badge/'.$skill['issue'].'/user/'.$userId;
1203
                    $skillList[$skill['id']] = array_merge($skill, $skillData);
1204
                } else {
1205
                    $skillList[$skill['id']] = $skill['id'];
1206
                }
1207
            }
1208
        }
1209
1210
        return $skillList;
1211
    }
1212
1213
    /**
1214
     * @param Vertex $vertex
1215
     * @param array $skills
1216
     * @param int $level
1217
     *
1218
     * @return string
1219
     */
1220
    public function processVertex(Vertex $vertex, $skills = [], $level = 0)
1221
    {
1222
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
1223
        $subTable = '';
1224
        if ($vertex->getVerticesEdgeTo()->count() > 0) {
1225
            if ($isHierarchicalTable) {
1226
                $subTable .= '<ul>';
1227
            }
1228
            foreach ($vertex->getVerticesEdgeTo() as $subVertex) {
1229
                $data = $subVertex->getAttribute('graphviz.data');
1230
                $passed = in_array($data['id'], array_keys($skills));
1231
                $transparency = '';
1232
                if ($passed === false) {
1233
                    // @todo use css class
1234
                    $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1235
                }
1236
1237
                if ($isHierarchicalTable) {
1238
                    $label = $this->processSkillListSimple([$data], 'mini', $transparency);
1239
                    $subTable .= '<li>'.$label;
1240
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
1241
                    $subTable .= '</li>';
1242
                } else {
1243
                    $imageSize = 'mini';
1244
                    if ($level == 2) {
1245
                        $imageSize = 'small';
1246
                    }
1247
                    $showTitle = true;
1248
                    if ($level > 2) {
1249
                        $showTitle = false;
1250
                    }
1251
1252
                    $label = $this->processSkillListSimple([$data], $imageSize, $transparency, true, $showTitle);
1253
                    $subTable .= '<div class="thumbnail" style="float:left; margin-right:5px; ">';
1254
                    $subTable .= '<div style="'.$transparency.'">';
1255
1256
                    $subTable .= '<div style="text-align: center">';
1257
                    $subTable .= $label;
1258
                    $subTable .= '</div>';
1259
1260
1261
                    $subTable .= '</div>';
1262
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
1263
                    $subTable .= '</div>';
1264
                }
1265
            }
1266
1267
            if ($isHierarchicalTable) {
1268
                $subTable .= '</ul>';
1269
            }
1270
        }
1271
1272
        return $subTable;
1273
    }
1274
1275
    /**
1276
     * @param int $userId
1277
     * @param int $courseId
1278
     * @param int $sessionId
1279
     * @param bool $addTitle
1280
     * @return array
1281
     */
1282
    public function getUserSkillsTable($userId, $courseId = 0, $sessionId = 0, $addTitle = true)
1283
    {
1284
        $skills = $this->getUserSkills($userId, true, $courseId, $sessionId);
1285
1286
        $courseTempList = [];
1287
        $tableRows = [];
1288
        $skillParents = [];
1289
        foreach ($skills as $resultData) {
1290
            $parents = $this->get_parents($resultData['id']);
1291
            foreach ($parents as $parentData) {
1292
                $parentData['passed'] = in_array($parentData['id'], array_keys($skills));
1293
                if ($parentData['passed'] && isset($skills[$parentData['id']]['url'])) {
1294
                    $parentData['data']['url'] = $skills[$parentData['id']]['url'];
1295
                }
1296
                $skillParents[$resultData['id']][$parentData['id']] = $parentData;
1297
            }
1298
        }
1299
1300
        foreach ($skills as $resultData) {
1301
            $courseId = $resultData['course_id'];
1302
            if (!empty($courseId)) {
1303
                if (isset($courseTempList[$courseId])) {
1304
                    $courseInfo = $courseTempList[$courseId];
1305
                } else {
1306
                    $courseInfo = api_get_course_info_by_id($courseId);
1307
                    $courseTempList[$courseId] = $courseInfo;
1308
                }
1309
            }
1310
1311
            $tableRow = [
1312
                'skill_badge' => $resultData['img_mini'],
1313
                'skill_name' => self::translateName($resultData['name']),
1314
                'short_code' => $resultData['short_code'],
1315
                'achieved_at' => api_get_local_time($resultData['acquired_skill_at']),
1316
                'course_image' => '',
1317
                'course_name' => '',
1318
            ];
1319
1320
            if (!empty($courseInfo)) {
1321
                $tableRow['course_image'] = $courseInfo['course_image_source'];
1322
                $tableRow['course_name'] = $courseInfo['title'];
1323
            }
1324
            $tableRows[] = $tableRow;
1325
        }
1326
1327
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
1328
        $allowLevels = api_get_configuration_value('skill_levels_names');
1329
1330
        $tableResult = '<div id="skillList">';
1331
        if ($isHierarchicalTable) {
1332
            $tableResult = '<div class="table-responsive">';
1333
        }
1334
1335
        if ($addTitle) {
1336
            $tableResult .= '
1337
                    <table class="table">
1338
                        <thead>
1339
                            <tr>
1340
                                <th>'.get_lang('AchievedSkills').'</th>
1341
                            </tr>
1342
                        </thead>
1343
                        <tbody>
1344
                        <tr><td>';
1345
        }
1346
1347
        if (!empty($skillParents)) {
1348
            if (empty($allowLevels)) {
1349
                $tableResult .= $this->processSkillListSimple($skills);
1350
            } else {
1351
                $graph = new Graph();
1352
                $graph->setAttribute('graphviz.graph.rankdir', 'LR');
1353
                foreach ($skillParents as $skillId => $parentList) {
1354
                    $old = null;
1355
                    foreach ($parentList as $parent) {
1356
                        if ($graph->hasVertex($parent['id'])) {
1357
                            $current = $graph->getVertex($parent['id']);
1358
                        } else {
1359
                            $current = $graph->createVertex($parent['id']);
1360
                            $current->setAttribute('graphviz.data', $parent['data']);
1361
                        }
1362
1363
                        if (!empty($old)) {
1364
                            if ($graph->hasVertex($old['id'])) {
1365
                                $nextVertex = $graph->getVertex($old['id']);
1366
                            } else {
1367
                                $nextVertex = $graph->createVertex($old['id']);
1368
                                $nextVertex->setAttribute('graphviz.data', $old['data']);
1369
                            }
1370
1371
                            if (!$nextVertex->hasEdgeTo($current)) {
1372
                                $nextVertex->createEdgeTo($current);
1373
                            }
1374
                        }
1375
                        $old = $parent;
1376
                    }
1377
                }
1378
1379
                if ($isHierarchicalTable) {
1380
                    $table = '<table class ="table table-bordered">';
1381
                    // Getting "root" vertex
1382
                    $root = $graph->getVertex(1);
1383
                    $table .= '<tr>';
1384
                    /** @var Vertex $vertex */
1385
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
1386
                        $data = $vertex->getAttribute('graphviz.data');
1387
1388
                        $passed = in_array($data['id'], array_keys($skills));
1389
                        $transparency = '';
1390
                        if ($passed === false) {
1391
                            // @todo use a css class
1392
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1393
                        }
1394
1395
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency);
1396
                        $table .= '<td >';
1397
1398
                        $table .= '<div class="skills_chart"> <ul><li>'.$label;
1399
                        $table .= $this->processVertex($vertex, $skills);
1400
                        $table .= '</ul></li></div>';
1401
                        $table .= '</td>';
1402
                    }
1403
                    $table .= '</tr></table>';
1404
                } else {
1405
                    // Getting "root" vertex
1406
                    $root = $graph->getVertex(1);
1407
                    $table = '';
1408
                    /** @var Vertex $vertex */
1409
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
1410
                        $data = $vertex->getAttribute('graphviz.data');
1411
1412
                        $passed = in_array($data['id'], array_keys($skills));
1413
                        $transparency = '';
1414
                        if ($passed === false) {
1415
                            // @todo use a css class
1416
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1417
                        }
1418
1419
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency, false);
1420
1421
                        $skillTable = $this->processVertex($vertex, $skills, 2);
1422
                        $table .= "<h3>$label</h3>";
1423
1424
                        if (!empty($skillTable)) {
1425
                            $table .= '<table class ="table table-bordered">';
1426
                            $table .= '<tr>';
1427
                            $table .= '<td>';
1428
                            $table .= '<div>';
1429
                            $table .= $skillTable;
1430
                            $table .= '</div>';
1431
                            $table .= '</td>';
1432
                            $table .= '</tr></table>';
1433
                        }
1434
                    }
1435
                }
1436
1437
                $tableResult .= $table;
1438
            }
1439
        } else {
1440
            $tableResult .= get_lang('WithoutAchievedSkills');
1441
        }
1442
1443
        if ($addTitle) {
1444
            $tableResult .= '</td>
1445
                            </tr>
1446
                        </tbody>
1447
                    </table>
1448
                ';
1449
        }
1450
        $tableResult .= '</div>';
1451
1452
        return [
1453
            'skills' => $tableRows,
1454
            'table' => $tableResult,
1455
        ];
1456
    }
1457
1458
    /**
1459
     * @param int $user_id
1460
     * @param int $skill_id
1461
     * @param bool $return_flat_array
1462
     * @param bool $add_root
1463
     * @return array|null
1464
     */
1465
    public function getSkillsTree(
1466
        $user_id = null,
1467
        $skill_id = null,
1468
        $return_flat_array = false,
1469
        $add_root = false
1470
    ) {
1471
        if ($skill_id == 1) {
1472
            $skill_id = 0;
1473
        }
1474
        if (isset($user_id) && !empty($user_id)) {
1475
            $skills = $this->get_all(true, $user_id, null, $skill_id);
1476
        } else {
1477
            $skills = $this->get_all(false, false, null, $skill_id);
1478
        }
1479
1480
        $original_skill = $this->list = $skills;
0 ignored issues
show
Bug Best Practice introduced by
The property list does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1481
1482
        // Show 1 item
1483
        if (!empty($skill_id)) {
1484
            if ($add_root) {
1485
                if (!empty($skill_id)) {
1486
                    // Default root node
1487
                    $skills[1] = [
1488
                        'id' => '1',
1489
                        'name' => get_lang('Root'),
1490
                        'parent_id' => '0',
1491
                    ];
1492
                    $skillInfo = $this->getSkillInfo($skill_id);
1493
1494
                    // 2nd node
1495
                    $skills[$skill_id] = $skillInfo;
1496
                    // Uncomment code below to hide the searched skill
1497
                    $skills[$skill_id]['data']['parent_id'] = $skillInfo['extra']['parent_id'];
1498
                    $skills[$skill_id]['parent_id'] = 1;
1499
                }
1500
            }
1501
        }
1502
1503
        $refs = [];
1504
        $skills_tree = null;
1505
1506
        // Create references for all nodes
1507
        $flat_array = [];
1508
        $family = [];
1509
        if (!empty($skills)) {
1510
            foreach ($skills as &$skill) {
1511
                if ($skill['parent_id'] == 0) {
1512
                    $skill['parent_id'] = 'root';
1513
                }
1514
1515
                // because except main keys (id, name, children) others keys
1516
                // are not saved while in the space tree
1517
                $skill['data'] = ['parent_id' => $skill['parent_id']];
1518
1519
                // If a short code was defined, send the short code to replace
1520
                // skill name (to shorten the text in the wheel)
1521
                if (!empty($skill['short_code']) &&
1522
                    api_get_setting('show_full_skill_name_on_skill_wheel') === 'false'
1523
                ) {
1524
                    $skill['data']['short_code'] = $skill['short_code'];
1525
                }
1526
1527
                $skill['data']['name'] = $skill['name'];
1528
                $skill['data']['status'] = $skill['status'];
1529
1530
                // In order to paint all members of a family with the same color
1531
                if (empty($skill_id)) {
1532
                    if ($skill['parent_id'] == 1) {
1533
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1534
                    }
1535
                } else {
1536
                    if ($skill['parent_id'] == $skill_id) {
1537
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1538
                    }
1539
                    /*if ($skill_id == $skill['id']) {
1540
                        $skill['parent_id'] = 1;
1541
                    }*/
1542
                }
1543
1544
                if (!isset($skill['data']['real_parent_id'])) {
1545
                    $skill['data']['real_parent_id'] = $skill['parent_id'];
1546
                }
1547
1548
                // User achieved the skill (depends in the gradebook with certification)
1549
                $skill['data']['achieved'] = false;
1550
                if ($user_id) {
1551
                    $skill['data']['achieved'] = $this->userHasSkill(
1552
                        $user_id,
1553
                        $skill['id']
1554
                    );
1555
                }
1556
1557
                // Check if the skill has related gradebooks
1558
                $skill['data']['skill_has_gradebook'] = false;
1559
                if (isset($skill['gradebooks']) && !empty($skill['gradebooks'])) {
1560
                    $skill['data']['skill_has_gradebook'] = true;
1561
                }
1562
                $refs[$skill['id']] = &$skill;
1563
                $flat_array[$skill['id']] = &$skill;
1564
            }
1565
1566
            // Checking family value
1567
1568
            $family_id = 1;
1569
            $new_family_array = [];
1570
            foreach ($family as $main_family_id => $family_items) {
1571
                if (!empty($family_items)) {
1572
                    foreach ($family_items as $item) {
1573
                        $new_family_array[$item['id']] = $family_id;
1574
                    }
1575
                }
1576
                $new_family_array[$main_family_id] = $family_id;
1577
                $family_id++;
1578
            }
1579
1580
            if (empty($original_skill)) {
1581
                $refs['root']['children'][0] = $skills[1];
1582
                $skills[$skill_id]['data']['family_id'] = 1;
1583
                $refs['root']['children'][0]['children'][0] = $skills[$skill_id];
1584
                $flat_array[$skill_id] = $skills[$skill_id];
1585
            } else {
1586
                // Moving node to the children index of their parents
1587
                foreach ($skills as $my_skill_id => &$skill) {
1588
                    if (isset($new_family_array[$skill['id']])) {
1589
                        $skill['data']['family_id'] = $new_family_array[$skill['id']];
1590
                    }
1591
                    $refs[$skill['parent_id']]['children'][] = &$skill;
1592
                    $flat_array[$my_skill_id] = $skill;
1593
                }
1594
            }
1595
1596
            $skills_tree = [
1597
                'name' => get_lang('SkillRootName'),
1598
                'id' => 'root',
1599
                'children' => $refs['root']['children'],
1600
                'data' => [],
1601
            ];
1602
        }
1603
1604
        if ($return_flat_array) {
1605
            return $flat_array;
1606
        }
1607
        unset($skills);
1608
1609
        return $skills_tree;
1610
    }
1611
1612
    /**
1613
     * Get skills tree as a simplified JSON structure
1614
     * @param int user id
1615
     * @param int skill id
1616
     * @param bool return a flat array or not
1617
     * @param int depth of the skills
1618
     * @return string json
1619
     */
1620
    public function getSkillsTreeToJson(
1621
        $user_id = null,
1622
        $skill_id = null,
1623
        $return_flat_array = false,
1624
        $main_depth = 2
1625
    ) {
1626
        $tree = $this->getSkillsTree(
1627
            $user_id,
1628
            $skill_id,
1629
            $return_flat_array,
1630
            true
1631
        );
1632
        $simple_tree = [];
1633
        if (!empty($tree['children'])) {
1634
            foreach ($tree['children'] as $element) {
1635
                $children = [];
1636
                if (isset($element['children'])) {
1637
                    $children = $this->getSkillToJson($element['children'], 1, $main_depth);
1638
                }
1639
                $simple_tree[] = [
1640
                    'name' => $element['name'],
1641
                    'children' => $children,
1642
                ];
1643
            }
1644
        }
1645
1646
        return json_encode($simple_tree[0]['children']);
1647
    }
1648
1649
    /**
1650
     * Get JSON element
1651
     * @param array $subtree
1652
     * @param int $depth
1653
     * @param int $max_depth
1654
     * @return array|null
1655
     */
1656
    public function getSkillToJson($subtree, $depth = 1, $max_depth = 2)
1657
    {
1658
        $simple_sub_tree = [];
1659
        if (is_array($subtree)) {
1660
            $counter = 1;
1661
            foreach ($subtree as $elem) {
1662
                $tmp = [];
1663
                $tmp['name'] = $elem['name'];
1664
                $tmp['id'] = $elem['id'];
1665
                $tmp['isSearched'] = self::isSearched($elem['id']);
1666
1667
                if (isset($elem['children']) && is_array($elem['children'])) {
1668
                    $tmp['children'] = $this->getSkillToJson(
1669
                        $elem['children'],
1670
                        $depth + 1,
1671
                        $max_depth
1672
                    );
1673
                } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
1674
                    //$tmp['colour'] = $this->colours[$depth][rand(0,3)];
1675
                }
1676
1677
                if ($depth > $max_depth) {
1678
                    continue;
1679
                }
1680
1681
                $tmp['depth'] = $depth;
1682
                $tmp['counter'] = $counter;
1683
                $counter++;
1684
1685
                if (isset($elem['data']) && is_array($elem['data'])) {
1686
                    foreach ($elem['data'] as $key => $item) {
1687
                        $tmp[$key] = $item;
1688
                    }
1689
                }
1690
                $simple_sub_tree[] = $tmp;
1691
            }
1692
            return $simple_sub_tree;
1693
        }
1694
        return null;
1695
    }
1696
1697
    /**
1698
     * @param int $user_id
1699
     * @return bool
1700
     */
1701
    public function getUserSkillRanking($user_id)
1702
    {
1703
        $user_id = intval($user_id);
1704
        $sql = "SELECT count(skill_id) count FROM {$this->table} s
1705
                INNER JOIN {$this->table_skill_rel_user} su
1706
                ON (s.id = su.skill_id)
1707
                WHERE user_id = $user_id";
1708
        $result = Database::query($sql);
1709
        if (Database::num_rows($result)) {
1710
            $result = Database::fetch_row($result);
1711
            return $result[0];
1712
        }
1713
        return false;
1714
    }
1715
1716
    /**
1717
     * @param $start
1718
     * @param $limit
1719
     * @param $sidx
1720
     * @param $sord
1721
     * @param $where_condition
1722
     * @return array
1723
     */
1724
    public function getUserListSkillRanking(
1725
        $start,
1726
        $limit,
1727
        $sidx,
0 ignored issues
show
Unused Code introduced by
The parameter $sidx is not used and could be removed. ( Ignorable by Annotation )

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

1727
        /** @scrutinizer ignore-unused */ $sidx,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1728
        $sord,
0 ignored issues
show
Unused Code introduced by
The parameter $sord is not used and could be removed. ( Ignorable by Annotation )

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

1728
        /** @scrutinizer ignore-unused */ $sord,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1729
        $where_condition
1730
    ) {
1731
        $start = intval($start);
1732
        $limit = intval($limit);
1733
        /*  ORDER BY $sidx $sord */
1734
        $sql = "SELECT *, @rownum:=@rownum+1 rank FROM (
1735
                    SELECT u.user_id, firstname, lastname, count(username) skills_acquired
1736
                    FROM {$this->table} s INNER JOIN {$this->table_skill_rel_user} su ON (s.id = su.skill_id)
1737
                    INNER JOIN {$this->table_user} u ON u.user_id = su.user_id, (SELECT @rownum:=0) r
1738
                    WHERE 1=1 $where_condition
1739
                    GROUP BY username
1740
                    ORDER BY skills_acquired desc
1741
                    LIMIT $start , $limit)  AS T1, (SELECT @rownum:=0) r";
1742
        $result = Database::query($sql);
1743
        if (Database::num_rows($result)) {
1744
            return Database::store_result($result, 'ASSOC');
1745
        }
1746
1747
        return [];
1748
    }
1749
1750
    /**
1751
     * @return int
1752
     */
1753
    public function getUserListSkillRankingCount()
1754
    {
1755
        $sql = "SELECT count(*) FROM (
1756
                    SELECT count(distinct 1)
1757
                    FROM {$this->table} s
1758
                    INNER JOIN {$this->table_skill_rel_user} su
1759
                    ON (s.id = su.skill_id)
1760
                    INNER JOIN {$this->table_user} u
1761
                    ON u.user_id = su.user_id
1762
                    GROUP BY username
1763
                 ) as T1";
1764
        $result = Database::query($sql);
1765
        if (Database::num_rows($result)) {
1766
            $result = Database::fetch_row($result);
1767
1768
            return $result[0];
1769
        }
1770
1771
        return 0;
1772
    }
1773
1774
    /**
1775
     * @param string $courseCode
1776
     * @return int
1777
     */
1778
    public function getCountSkillsByCourse($courseCode)
1779
    {
1780
        $courseCode = Database::escape_string($courseCode);
1781
        $sql = "SELECT count(skill_id) as count
1782
                FROM {$this->table_gradebook} g
1783
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1784
                ON g.id = sg.gradebook_id
1785
                WHERE course_code = '$courseCode'";
1786
1787
        $result = Database::query($sql);
1788
        if (Database::num_rows($result)) {
1789
            $result = Database::fetch_row($result);
1790
            return $result[0];
1791
        }
1792
        return 0;
1793
    }
1794
1795
    /**
1796
     * @param int $skillId
1797
     * @return array
1798
     */
1799
    public function getCoursesBySkill($skillId)
1800
    {
1801
        $skillId = intval($skillId);
1802
        $sql = "SELECT c.title, c.code
1803
                FROM {$this->table_gradebook} g
1804
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1805
                ON g.id = sg.gradebook_id
1806
                INNER JOIN {$this->table_course} c
1807
                ON c.code = g.course_code
1808
                WHERE sg.skill_id = $skillId
1809
                AND (g.session_id IS NULL OR g.session_id = 0)";
1810
        $result = Database::query($sql);
1811
1812
        return Database::store_result($result, 'ASSOC');
1813
    }
1814
1815
    /**
1816
     * Check if the user has the skill
1817
     * @param int $userId The user id
1818
     * @param int $skillId The skill id
1819
     * @param int $courseId Optional. The course id
1820
     * @param int $sessionId Optional. The session id
1821
     * @return boolean Whether the user has the skill return true. Otherwise return false
1822
     */
1823
    public function userHasSkill($userId, $skillId, $courseId = 0, $sessionId = 0)
1824
    {
1825
        $courseId = intval($courseId);
1826
        $sessionId = intval($sessionId);
1827
1828
        $whereConditions = [
1829
            'user_id = ? ' => intval($userId),
1830
            'AND skill_id = ? ' => intval($skillId),
1831
        ];
1832
1833
        if ($courseId > 0) {
1834
            $whereConditions['AND course_id = ? '] = $courseId;
1835
            $whereConditions['AND session_id = ? '] = $sessionId ? $sessionId : null;
1836
        }
1837
1838
        $result = Database::select(
1839
            'COUNT(1) AS qty',
1840
            $this->table_skill_rel_user,
1841
            [
1842
                'where' => $whereConditions,
1843
            ],
1844
            'first'
1845
        );
1846
1847
        if ($result != false) {
1848
            if ($result['qty'] > 0) {
1849
                return true;
1850
            }
1851
        }
1852
1853
        return false;
1854
    }
1855
1856
    /**
1857
     * Check if a skill is searched
1858
     * @param int $id The skill id
1859
     * @return boolean Whether el skill is searched return true. Otherwise return false
1860
     */
1861
    public static function isSearched($id)
1862
    {
1863
        $id = intval($id);
1864
1865
        if (empty($id)) {
1866
            return false;
1867
        }
1868
1869
        $skillRelProfileTable = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
1870
1871
        $result = Database::select(
1872
            'COUNT( DISTINCT `skill_id`) AS qty',
1873
            $skillRelProfileTable,
1874
            [
1875
                'where' => [
1876
                    'skill_id = ?' => $id,
1877
                ],
1878
            ],
1879
            'first'
1880
        );
1881
1882
        if ($result === false) {
1883
            return false;
1884
        }
1885
1886
        if ($result['qty'] > 0) {
1887
            return true;
1888
        }
1889
1890
        return false;
1891
    }
1892
1893
    /**
1894
     * Get the achieved skills by course
1895
     * @param int $courseId The course id
1896
     * @return array The skills list
1897
     */
1898
    public function listAchievedByCourse($courseId)
1899
    {
1900
        $courseId = intval($courseId);
1901
1902
        if ($courseId == 0) {
1903
            return [];
1904
        }
1905
1906
        $list = [];
1907
1908
        $sql = "SELECT
1909
                    course.id c_id,
1910
                    course.title c_name,
1911
                    course.directory c_directory,
1912
                    user.user_id,
1913
                    user.lastname,
1914
                    user.firstname,
1915
                    user.username,
1916
                    skill.id skill_id,
1917
                    skill.name skill_name,
1918
                    sru.acquired_skill_at
1919
                FROM {$this->table_skill_rel_user} AS sru
1920
                INNER JOIN {$this->table_course}
1921
                ON sru.course_id = course.id
1922
                INNER JOIN {$this->table_user}
1923
                ON sru.user_id = user.user_id
1924
                INNER JOIN {$this->table}
1925
                ON sru.skill_id = skill.id
1926
                WHERE course.id = $courseId";
1927
1928
        $result = Database::query($sql);
1929
1930
        while ($row = Database::fetch_assoc($result)) {
1931
            $row['skill_name'] = self::translateName($row['skill_name']);
1932
            $list[] = $row;
1933
        }
1934
1935
        return $list;
1936
    }
1937
1938
    /**
1939
     * Get the users list who achieved a skill
1940
     * @param int $skillId The skill id
1941
     *
1942
     * @return array The users list
1943
     */
1944
    public function listUsersWhoAchieved($skillId)
1945
    {
1946
        $skillId = intval($skillId);
1947
1948
        if ($skillId == 0) {
1949
            return [];
1950
        }
1951
1952
        $list = [];
1953
        $sql = "SELECT
1954
                    course.id c_id,
1955
                    course.title c_name,
1956
                    course.directory c_directory,
1957
                    user.user_id,
1958
                    user.lastname,
1959
                    user.firstname,
1960
                    user.username,
1961
                    skill.id skill_id,
1962
                    skill.name skill_name,
1963
                    sru.acquired_skill_at
1964
                FROM {$this->table_skill_rel_user} AS sru
1965
                INNER JOIN {$this->table_course}
1966
                ON sru.course_id = course.id
1967
                INNER JOIN {$this->table_user}
1968
                ON sru.user_id = user.user_id
1969
                INNER JOIN {$this->table}
1970
                ON sru.skill_id = skill.id
1971
                WHERE skill.id = $skillId ";
1972
1973
        $result = Database::query($sql);
1974
        while ($row = Database::fetch_assoc($result)) {
1975
            $row['skill_name'] = self::translateName($row['skill_name']);
1976
            $list[] = $row;
1977
        }
1978
1979
        return $list;
1980
    }
1981
1982
    /**
1983
     * Get the session list where the user can achieve a skill
1984
     * @param int $skillId The skill id
1985
     * @return array
1986
     */
1987
    public function getSessionsBySkill($skillId)
1988
    {
1989
        $skillId = intval($skillId);
1990
1991
        $sql = "SELECT s.id, s.name
1992
                FROM {$this->table_gradebook} g
1993
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1994
                ON g.id = sg.gradebook_id
1995
                INNER JOIN {$this->sessionTable} s
1996
                ON g.session_id = s.id
1997
                WHERE sg.skill_id = $skillId
1998
                AND g.session_id > 0";
1999
2000
        $result = Database::query($sql);
2001
2002
        return Database::store_result($result, 'ASSOC');
2003
    }
2004
2005
    /**
2006
     * Check if the $fromUser can comment the $toUser skill issue
2007
     * @param User $fromUser
2008
     * @param User $toUser
2009
     * @return boolean
2010
     */
2011
    public static function userCanAddFeedbackToUser($fromUser, $toUser)
2012
    {
2013
        if (api_is_platform_admin()) {
2014
            return true;
2015
        }
2016
2017
        $entityManager = Database::getManager();
2018
        /** @var UserRepository $userRepo */
2019
        $userRepo = $entityManager->getRepository('ChamiloUserBundle:User');
2020
        $fromUserStatus = $fromUser->getStatus();
2021
2022
        switch ($fromUserStatus) {
2023
            case SESSIONADMIN:
2024
                if (api_get_setting('allow_session_admins_to_manage_all_sessions') === 'true') {
2025
                    if ($toUser->getCreatorId() === $fromUser->getId()) {
2026
                        return true;
2027
                    }
2028
                }
2029
2030
                $sessionAdmins = $userRepo->getSessionAdmins($toUser);
2031
2032
                foreach ($sessionAdmins as $sessionAdmin) {
2033
                    if ($sessionAdmin->getId() !== $fromUser->getId()) {
2034
                        continue;
2035
                    }
2036
2037
                    return true;
2038
                }
2039
                break;
2040
            case STUDENT_BOSS:
2041
                $studentBosses = $userRepo->getStudentBosses($toUser);
2042
                foreach ($studentBosses as $studentBoss) {
2043
                    if ($studentBoss->getId() !== $fromUser->getId()) {
2044
                        continue;
2045
                    }
2046
2047
                    return true;
2048
                }
2049
                break;
2050
            case DRH:
2051
                return UserManager::is_user_followed_by_drh(
2052
                    $toUser->getId(),
2053
                    $fromUser->getId()
2054
                );
2055
        }
2056
2057
        return false;
2058
    }
2059
2060
    /**
2061
     * If $studentId is set then check if current user has the right to see
2062
     * the page.
2063
     * @param int $studentId check if current user has access to see $studentId
2064
     * @param bool $blockPage raise a api_not_allowed()
2065
     *
2066
     * @return bool
2067
     */
2068
    public static function isAllowed($studentId = 0, $blockPage = true)
2069
    {
2070
        if (self::isToolAvailable()) {
2071
            if (api_is_platform_admin()) {
2072
                return true;
2073
            }
2074
2075
            if (!empty($studentId)) {
2076
                $currentUserId = api_get_user_id();
2077
                if ((int) $currentUserId === (int) $studentId) {
2078
                    return true;
2079
                }
2080
2081
                $haveAccess = self::hasAccessToUserSkill(
2082
                    $currentUserId,
2083
                    $studentId
2084
                );
2085
2086
                if ($haveAccess) {
2087
                    return true;
2088
                }
2089
            }
2090
        }
2091
2092
        if ($blockPage) {
2093
            api_not_allowed(true);
2094
        }
2095
2096
        return false;
2097
    }
2098
2099
    /**
2100
     * @return bool
2101
     */
2102
    public static function isToolAvailable()
2103
    {
2104
        $allowTool = api_get_setting('allow_skills_tool');
2105
2106
        if ($allowTool === 'true') {
2107
            return true;
2108
        }
2109
2110
        return false;
2111
    }
2112
2113
    /**
2114
     * @param $currentUserId
2115
     * @param $studentId
2116
     * @return bool
2117
     */
2118
    public static function hasAccessToUserSkill($currentUserId, $studentId)
2119
    {
2120
        if (self::isToolAvailable()) {
2121
            if (api_is_platform_admin()) {
2122
                return true;
2123
            }
2124
2125
            if (api_is_student_boss()) {
2126
                $isBoss = UserManager::userIsBossOfStudent($currentUserId, $studentId);
2127
                if ($isBoss) {
2128
                    return true;
2129
                }
2130
            }
2131
2132
            $allow = api_get_configuration_value('allow_private_skills');
2133
            if ($allow === true) {
2134
                if (api_is_teacher()) {
2135
                    return UserManager::isTeacherOfStudent(
2136
                        $currentUserId,
2137
                        $studentId
2138
                    );
2139
                }
2140
2141
                if (api_is_drh()) {
2142
                    return UserManager::is_user_followed_by_drh(
2143
                        $studentId,
2144
                        $currentUserId
2145
                    );
2146
                }
2147
            }
2148
        }
2149
2150
        return false;
2151
    }
2152
2153
    /**
2154
     * Get skills
2155
     * @param int $userId
2156
     * @param int level
2157
     * @return array
2158
     */
2159
    public function getStudentSkills($userId, $level = 0)
2160
    {
2161
        $sql = "SELECT s.id, s.name, sru.acquired_skill_at
2162
                FROM {$this->table} s
2163
                INNER JOIN {$this->table_skill_rel_user} sru
2164
                ON s.id = sru.skill_id
2165
                WHERE sru.user_id = $userId";
2166
2167
        $result = Database::query($sql);
2168
2169
        $skills = [];
2170
        foreach ($result as $item) {
2171
            if (empty($level)) {
2172
                $skills[] = [
2173
                    'name' => self::translateName($item['name']),
2174
                    'acquired_skill_at' => $item['acquired_skill_at'],
2175
                ];
2176
            } else {
2177
                $parents = self::get_parents($item['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::get_parents() 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

2177
                /** @scrutinizer ignore-call */ 
2178
                $parents = self::get_parents($item['id']);
Loading history...
2178
                // +2 because it takes into account the root
2179
                if (count($parents) == $level + 1) {
2180
                    $skills[] = [
2181
                        'name' => self::translateName($item['name']),
2182
                        'acquired_skill_at' => $item['acquired_skill_at'],
2183
                    ];
2184
                }
2185
            }
2186
        }
2187
2188
        return $skills;
2189
    }
2190
2191
    /**
2192
     * @param string $name
2193
     * @return string
2194
     */
2195
    public static function translateName($name)
2196
    {
2197
        $variable = ChamiloApi::getLanguageVar($name, 'Skill');
2198
2199
        return isset($GLOBALS[$variable]) ? $GLOBALS[$variable] : $name;
2200
    }
2201
2202
    /**
2203
     * @param string $code
2204
     * @return mixed|string
2205
     */
2206
    public static function translateCode($code)
2207
    {
2208
        if (empty($code)) {
2209
            return '';
2210
        }
2211
2212
        $variable = ChamiloApi::getLanguageVar($code, 'SkillCode');
2213
2214
        return isset($GLOBALS[$variable]) ? $GLOBALS[$variable] : $code;
2215
    }
2216
2217
    /**
2218
     * @param FormValidator $form
2219
     * @param array $skillInfo
2220
     *
2221
     * @return array
2222
     */
2223
    public function setForm(FormValidator &$form, $skillInfo = [])
2224
    {
2225
        $allSkills = $this->get_all();
2226
        $objGradebook = new Gradebook();
2227
2228
        $isAlreadyRootSkill = false;
2229
        foreach ($allSkills as $checkedSkill) {
2230
            if (intval($checkedSkill['parent_id']) > 0) {
2231
                $isAlreadyRootSkill = true;
2232
                break;
2233
            }
2234
        }
2235
2236
        $skillList = $isAlreadyRootSkill ? [] : [0 => get_lang('None')];
2237
2238
        foreach ($allSkills as $skill) {
2239
            if (isset($skillInfo['id']) && $skill['id'] == $skillInfo['id']) {
2240
                continue;
2241
            }
2242
2243
            $skillList[$skill['id']] = $skill['name'];
2244
        }
2245
2246
        $allGradeBooks = $objGradebook->find('all');
2247
2248
        // This procedure is for check if there is already a Skill with no Parent (Root by default)
2249
        $gradeBookList = [];
2250
        foreach ($allGradeBooks as $gradebook) {
2251
            $gradeBookList[$gradebook['id']] = $gradebook['name'];
2252
        }
2253
2254
        $translateUrl = api_get_path(WEB_CODE_PATH).'admin/skill_translate.php?';
2255
        $translateNameButton = '';
2256
        $translateCodeButton = '';
2257
        $skillId = null;
2258
        if (!empty($skillInfo)) {
2259
            $skillId = $skillInfo['id'];
2260
            $translateNameUrl = $translateUrl.http_build_query(['skill' => $skillId, 'action' => 'name']);
2261
            $translateCodeUrl = $translateUrl.http_build_query(['skill' => $skillId, 'action' => 'code']);
2262
            $translateNameButton = Display::toolbarButton(
2263
                get_lang('TranslateThisTerm'),
2264
                $translateNameUrl,
2265
                'language',
2266
                'link'
2267
            );
2268
            $translateCodeButton = Display::toolbarButton(
2269
                get_lang('TranslateThisTerm'),
2270
                $translateCodeUrl,
2271
                'language',
2272
                'link'
2273
            );
2274
        }
2275
2276
        $form->addText('name', [get_lang('Name'), $translateNameButton], true, ['id' => 'name']);
2277
        $form->addText('short_code', [get_lang('ShortCode'), $translateCodeButton], false, ['id' => 'short_code']);
2278
2279
        // Cannot change parent of root
2280
        if ($skillId != 1) {
2281
            $form->addSelect('parent_id', get_lang('Parent'), $skillList, ['id' => 'parent_id']);
2282
        }
2283
2284
        $form->addSelect(
2285
            'gradebook_id',
2286
            [get_lang('Gradebook'), get_lang('WithCertificate')],
2287
            $gradeBookList,
2288
            ['id' => 'gradebook_id', 'multiple' => 'multiple', 'size' => 10]
2289
        );
2290
        $form->addTextarea('description', get_lang('Description'), ['id' => 'description', 'rows' => 7]);
2291
        $form->addTextarea('criteria', get_lang('CriteriaToEarnTheBadge'), ['id' => 'criteria', 'rows' => 7]);
2292
2293
        // EXTRA FIELDS
2294
        $extraField = new ExtraField('skill');
2295
        $returnParams = $extraField->addElements($form, $skillId);
2296
2297
        if (empty($skillInfo)) {
2298
            $form->addButtonCreate(get_lang('Add'));
2299
        } else {
2300
            $form->addButtonUpdate(get_lang('Update'));
2301
        }
2302
        $form->addHidden('id', null);
2303
2304
        return $returnParams;
2305
    }
2306
2307
    /**
2308
     * @return string
2309
     */
2310
    public function getToolBar()
2311
    {
2312
        $toolbar = Display::url(
2313
            Display::return_icon(
2314
                'back.png',
2315
                get_lang('ManageSkills'),
2316
                null,
2317
                ICON_SIZE_MEDIUM
2318
            ),
2319
            api_get_path(WEB_CODE_PATH).'admin/skill_list.php'
2320
        );
2321
        $actions = '<div class="actions">'.$toolbar.'</div>';
2322
2323
        return $actions;
2324
    }
2325
2326
    /**
2327
     * @param \Chamilo\SkillBundle\Entity\SkillRelItem $skillRelItem
2328
     * @param \Chamilo\SkillBundle\Entity\SkillRelItemRelUser $skillRelItemRelUser
2329
     * @param bool $addHeader
2330
     * @return string
2331
     */
2332
    public static function getUserSkillStatusLabel($skillRelItem, $skillRelItemRelUser, $addHeader = true)
2333
    {
2334
        if (empty($skillRelItem)) {
2335
            return '';
2336
        }
2337
        $type = 'success';
2338
        if (empty($skillRelItemRelUser)) {
2339
            $type = 'danger';
2340
        }
2341
        $label = '';
2342
        $skill = $skillRelItem->getSkill();
2343
        if ($addHeader) {
2344
            $label .= '<span id="'.$skill->getId().'" class="user_skill" style="cursor:pointer">';
2345
        }
2346
        $label .= Display::label($skill->getName(), $type);
2347
        if ($addHeader) {
2348
            $label .= '</span>&nbsp;';
2349
        }
2350
2351
        return $label;
2352
    }
2353
2354
    /**
2355
     * Assign a user with a SkilRelItem object
2356
     *
2357
     * @param FormValidator $form
2358
     * @param int $typeId see ITEM_TYPE_* constants
2359
     * @param int $itemId
2360
     * @param int $userId
2361
     */
2362
    public static function addSkillsToUserForm(FormValidator $form, $typeId, $itemId, $userId)
2363
    {
2364
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2365
        if ($allowSkillInTools && !empty($typeId) && !empty($itemId) && !empty($userId)) {
2366
            $em = Database::getManager();
2367
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2368
                ['itemId' => $itemId, 'itemType' => $typeId]
2369
            );
2370
2371
            $skillRelUser = new SkillRelUser();
2372
            $skillUserList = $skillRelUser->getUserSkills($userId);
2373
            if (!empty($skillUserList)) {
2374
                $skillUserList = array_column($skillUserList, 'skill_id');
2375
            }
2376
2377
            $skills = '';
2378
            /** @var SkillRelItem $skillRelItem */
2379
            foreach ($items as $skillRelItem) {
2380
                $criteria = [
2381
                    'user' => $userId,
2382
                    'skillRelItem' => $skillRelItem
2383
                ];
2384
                $skillRelItemRelUser = $em->getRepository('ChamiloSkillBundle:SkillRelItemRelUser')->findOneBy($criteria);
2385
                $skills .= self::getUserSkillStatusLabel($skillRelItem, $skillRelItemRelUser);
2386
            }
2387
2388
            if (!empty($skills)) {
2389
                $url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=update_skill_rel_user&'.api_get_cidreq();
2390
                $params = [
2391
                    'item_id' => $itemId,
2392
                    'type_id' => $typeId,
2393
                    'user_id' => $userId,
2394
                    'course_id' => api_get_course_int_id(),
2395
                    'session_id' => api_get_session_id()
2396
                ];
2397
                $params = json_encode($params);
2398
                $html = '
2399
                <script>
2400
                    $(function() {
2401
                        $(".user_skill").on("click", function() {
2402
                            var skillId = this.id;
2403
                            var params = '.$params.';
2404
                            $.ajax({
2405
                                type: "GET",
2406
                                async: false,
2407
                                data: params,
2408
                                url: "'.$url.'&skill_id="+skillId,
2409
                                success: function(result) {
2410
                                    $("#" +skillId+ ".user_skill").html(result);
2411
                                }
2412
                            });                            
2413
                        });                        
2414
                    });
2415
                </script>
2416
                ';
2417
                $form->addHtml($html);
2418
                $form->addLabel(get_lang('Skills'), $skills);
2419
            }
2420
        }
2421
    }
2422
2423
    /**
2424
     * Add skills select ajax for an item (exercise, lp)
2425
     * @param FormValidator $form
2426
     * @param int $typeId see ITEM_TYPE_* constants
2427
     * @param int $itemId
2428
     * @throws Exception
2429
     * @return array
2430
     */
2431
    public static function addSkillsToForm(FormValidator $form, $typeId, $itemId = 0)
2432
    {
2433
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2434
        if (!$allowSkillInTools) {
2435
            return [];
2436
        }
2437
2438
        $skillList = [];
2439
        if (!empty($itemId)) {
2440
            $em = Database::getManager();
2441
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2442
                ['itemId' => $itemId, 'itemType' => $typeId]
2443
            );
2444
            /** @var SkillRelItem $skillRelItem */
2445
            foreach ($items as $skillRelItem) {
2446
                $skillList[$skillRelItem->getSkill()->getId()] = $skillRelItem->getSkill()->getName();
2447
            }
2448
        }
2449
2450
        $form->addSelectAjax(
2451
            'skills',
2452
            get_lang('Skills'),
2453
            $skillList,
2454
            [
2455
                'url' => api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=search_skills',
2456
                'multiple' => 'multiple',
2457
            ]
2458
        );
2459
2460
        return $skillList;
2461
    }
2462
2463
    /**
2464
     * @param int $courseId
2465
     * @param int $sessionId
2466
     * @return array
2467
     */
2468
    public static function getSkillRelItemsPerCourse($courseId, $sessionId = null)
2469
    {
2470
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2471
        $skills = [];
2472
2473
        if (empty($sessionId)) {
2474
            $sessionId = null;
2475
        }
2476
2477
        if ($allowSkillInTools) {
2478
            $em = Database::getManager();
2479
            $skills = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2480
                ['courseId' => $courseId, 'sessionId' => $sessionId]
2481
            );
2482
        }
2483
2484
        return $skills;
2485
    }
2486
2487
    /**
2488
     * @param int $itemId
2489
     * @param int $itemType
2490
     * @return array
2491
     */
2492
    public static function getItemInfo($itemId, $itemType)
2493
    {
2494
        $itemInfo = [];
2495
        $itemId = (int) $itemId;
2496
        $itemType = (int) $itemType;
2497
        $em = Database::getManager();
2498
2499
        switch ($itemType) {
2500
            case ITEM_TYPE_EXERCISE:
2501
                /** @var \Chamilo\CourseBundle\Entity\CQuiz $item */
2502
                $item = $em->getRepository('ChamiloCourseBundle:CQuiz')->find($itemId);
2503
                if ($item) {
2504
                    $itemInfo['name'] = $item->getTitle();
2505
                }
2506
                break;
2507
            case ITEM_TYPE_HOTPOTATOES:
2508
                break;
2509
            case ITEM_TYPE_LINK:
2510
                /** @var \Chamilo\CourseBundle\Entity\CLink $item */
2511
                $item = $em->getRepository('ChamiloCourseBundle:CLink')->find($itemId);
2512
                if ($item) {
2513
                    $itemInfo['name'] = $item->getTitle();
2514
                }
2515
                break;
2516
            case ITEM_TYPE_LEARNPATH:
2517
                /** @var \Chamilo\CourseBundle\Entity\CLp $item */
2518
                $item = $em->getRepository('ChamiloCourseBundle:CLp')->find($itemId);
2519
                if ($item) {
2520
                    $itemInfo['name'] = $item->getName();
2521
                }
2522
                break;
2523
            case ITEM_TYPE_GRADEBOOK:
2524
                break;
2525
            case ITEM_TYPE_STUDENT_PUBLICATION:
2526
                /** @var \Chamilo\CourseBundle\Entity\CStudentPublication $item */
2527
                $item = $em->getRepository('ChamiloCourseBundle:CStudentPublication')->find($itemId);
2528
                if ($item) {
2529
                    $itemInfo['name'] = $item->getTitle();
2530
                }
2531
                break;
2532
            //ITEM_TYPE_FORUM', 7);
2533
            case ITEM_TYPE_ATTENDANCE:
2534
                /** @var \Chamilo\CourseBundle\Entity\CAttendance $item */
2535
                $item = $em->getRepository('ChamiloCourseBundle:CAttendance')->find($itemId);
2536
                if ($item) {
2537
                    $itemInfo['name'] = $item->getName();
2538
                }
2539
                break;
2540
            case ITEM_TYPE_SURVEY:
2541
                /** @var \Chamilo\CourseBundle\Entity\CSurvey $item */
2542
                $item = $em->getRepository('ChamiloCourseBundle:CSurvey')->find($itemId);
2543
                if ($item) {
2544
                    $itemInfo['name'] = strip_tags($item->getTitle());
2545
                }
2546
                break;
2547
            case ITEM_TYPE_FORUM_THREAD:
2548
                /** @var \Chamilo\CourseBundle\Entity\CForumThread $item */
2549
                $item = $em->getRepository('ChamiloCourseBundle:CForumThread')->find($itemId);
2550
                if ($item) {
2551
                    $itemInfo['name'] = $item->getThreadTitle();
2552
                }
2553
                break;
2554
        }
2555
2556
        return $itemInfo;
2557
    }
2558
2559
2560
    /**
2561
     * @param int $typeId
2562
     * @param int $itemId
2563
     * @return array
2564
     */
2565
    public static function getSkillRelItems($typeId, $itemId)
2566
    {
2567
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2568
        $skills = [];
2569
        if ($allowSkillInTools) {
2570
            $em = Database::getManager();
2571
            $skills = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2572
                ['itemId' => $itemId, 'itemType' => $typeId]
2573
            );
2574
        }
2575
2576
        return $skills;
2577
    }
2578
2579
    /**
2580
     * @param int $typeId
2581
     * @param int $itemId
2582
     *
2583
     * @return string
2584
     */
2585
    public static function getSkillRelItemsToString($typeId, $itemId)
2586
    {
2587
        $skills = self::getSkillRelItems($typeId, $itemId);
2588
        $skillToString = '';
2589
        if (!empty($skills)) {
2590
            /** @var SkillRelItem $skillRelItem */
2591
            $skillList = [];
2592
            foreach ($skills as $skillRelItem) {
2593
                $skillList[] = Display::label($skillRelItem->getSkill()->getName(), 'success');
2594
            }
2595
            $skillToString = '&nbsp;'.implode(' ', $skillList);
2596
        }
2597
2598
        return $skillToString;
2599
    }
2600
2601
    /**
2602
     * @param int $itemId
2603
     * @param int $typeId
2604
     */
2605
    public static function deleteSkillsFromItem($itemId, $typeId)
2606
    {
2607
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2608
        if ($allowSkillInTools) {
2609
            $itemId = (int) $itemId;
2610
            $typeId = (int) $typeId;
2611
2612
            $em = Database::getManager();
2613
            // Delete old ones
2614
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2615
                ['itemId' => $itemId, 'itemType' => $typeId]
2616
            );
2617
2618
            /** @var SkillRelItem $skillRelItem */
2619
            foreach ($items as $skillRelItem) {
2620
                $em->remove($skillRelItem);
2621
            }
2622
            $em->flush();
2623
        }
2624
    }
2625
2626
    /**
2627
     * Relate skill with an item (exercise, gradebook, lp, etc)
2628
     * @param FormValidator $form
2629
     * @param int $typeId
2630
     * @param int $itemId
2631
     * @throws \Doctrine\ORM\OptimisticLockException
2632
     */
2633
    public static function saveSkills($form, $typeId, $itemId)
2634
    {
2635
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2636
        if ($allowSkillInTools) {
2637
            $userId = api_get_user_id();
2638
            $courseId = api_get_course_int_id();
2639
            if (empty($courseId)) {
2640
                $courseId = null;
2641
            }
2642
            $sessionId = api_get_session_id();
2643
            if (empty($sessionId)) {
2644
                $sessionId = null;
2645
            }
2646
2647
            $em = Database::getManager();
2648
            $skills = (array) $form->getSubmitValue('skills');
2649
2650
            // Delete old ones
2651
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2652
                ['itemId' => $itemId, 'itemType' => $typeId]
2653
            );
2654
            if (!empty($items)) {
2655
                /** @var SkillRelItem $skillRelItem */
2656
                foreach ($items as $skillRelItem) {
2657
                    if (!in_array($skillRelItem->getSkill()->getId(), $skills)) {
2658
                        $em->remove($skillRelItem);
2659
                    }
2660
                }
2661
                $em->flush();
2662
            }
2663
2664
            // Add new one
2665
            if (!empty($skills)) {
2666
                foreach ($skills as $skillId) {
2667
                    /** @var \Chamilo\CoreBundle\Entity\Skill $skill */
2668
                    $skill = $em->getRepository('ChamiloCoreBundle:Skill')->find($skillId);
2669
                    if ($skill) {
2670
                        if (!$skill->hasItem($typeId, $itemId)) {
0 ignored issues
show
Bug introduced by
The method hasItem() does not exist on Chamilo\CoreBundle\Entity\Skill. ( Ignorable by Annotation )

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

2670
                        if (!$skill->/** @scrutinizer ignore-call */ hasItem($typeId, $itemId)) {

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...
2671
                            $skillRelItem = new SkillRelItem();
2672
                            $skillRelItem
2673
                                ->setItemType($typeId)
2674
                                ->setItemId($itemId)
2675
                                ->setCourseId($courseId)
2676
                                ->setSessionId($sessionId)
2677
                                ->setCreatedBy($userId)
2678
                                ->setUpdatedBy($userId)
2679
                            ;
2680
                            $skill->addItem($skillRelItem);
0 ignored issues
show
Bug introduced by
The method addItem() does not exist on Chamilo\CoreBundle\Entity\Skill. ( Ignorable by Annotation )

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

2680
                            $skill->/** @scrutinizer ignore-call */ 
2681
                                    addItem($skillRelItem);

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...
2681
                            $em->persist($skill);
2682
                            $em->flush();
2683
                        }
2684
                    }
2685
                }
2686
            }
2687
        }
2688
    }
2689
}
2690