Passed
Push — master ( 447a4c...2f567c )
by Julito
09:40
created

Skill::addSkillToUserBadge()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 21
c 0
b 0
f 0
nc 3
nop 5
dl 0
loc 34
rs 9.584
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
5
use Chamilo\CoreBundle\Entity\Skill as SkillEntity;
6
use Chamilo\CoreBundle\Entity\SkillRelUser as SkillRelUserEntity;
7
use Chamilo\SkillBundle\Entity\SkillRelCourse;
8
use Chamilo\SkillBundle\Entity\SkillRelItem;
9
use Chamilo\UserBundle\Entity\User;
10
use Fhaculty\Graph\Graph;
11
use Fhaculty\Graph\Vertex;
12
13
/**
14
 * Class SkillProfile.
15
 *
16
 * @todo break the file in different classes
17
 *
18
 * @package chamilo.library
19
 */
20
class SkillProfile extends Model
21
{
22
    public $columns = ['id', 'name', 'description'];
23
24
    /**
25
     * Constructor.
26
     */
27
    public function __construct()
28
    {
29
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_PROFILE);
30
        $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...
31
    }
32
33
    /**
34
     * @return array
35
     */
36
    public function getProfiles()
37
    {
38
        $sql = "SELECT * FROM $this->table p
39
                INNER JOIN $this->table_rel_profile sp
40
                ON (p.id = sp.profile_id) ";
41
        $result = Database::query($sql);
42
        $profiles = Database::store_result($result, 'ASSOC');
43
44
        return $profiles;
45
    }
46
47
    /**
48
     * This function is for editing profile info from profile_id.
49
     *
50
     * @param int    $profileId
51
     * @param string $name
52
     * @param string $description
53
     *
54
     * @return bool
55
     */
56
    public function updateProfileInfo($profileId, $name, $description)
57
    {
58
        $profileId = (int) $profileId;
59
60
        if (empty($profileId)) {
61
            return false;
62
        }
63
64
        $name = Database::escape_string($name);
65
        $description = Database::escape_string($description);
66
67
        $sql = "UPDATE $this->table SET
68
                    name = '$name',
69
                    description = '$description'
70
                WHERE id = $profileId ";
71
        Database::query($sql);
72
73
        return true;
74
    }
75
76
    /**
77
     * Call the save method of the parent class and the SkillRelProfile object.
78
     *
79
     * @param array $params
80
     * @param bool  $show_query Whether to show the query in parent save() method
81
     *
82
     * @return mixed Profile ID or false if incomplete params
83
     */
84
    public function save($params, $show_query = false)
85
    {
86
        if (!empty($params)) {
87
            $profile_id = parent::save($params, $show_query);
88
            if ($profile_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $profile_id of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
89
                $skill_rel_profile = new SkillRelProfile();
90
                if (isset($params['skills'])) {
91
                    foreach ($params['skills'] as $skill_id) {
92
                        $attributes = [
93
                            'skill_id' => $skill_id,
94
                            'profile_id' => $profile_id,
95
                        ];
96
                        $skill_rel_profile->save($attributes);
97
                    }
98
                }
99
100
                return $profile_id;
101
            }
102
        }
103
104
        return false;
105
    }
106
107
    /**
108
     * Delete a skill profile.
109
     *
110
     * @param int $id The skill profile id
111
     *
112
     * @return bool Whether delete a skill profile
113
     */
114
    public function delete($id)
115
    {
116
        Database::delete(
117
            $this->table_rel_profile,
118
            [
119
                'profile_id' => $id,
120
            ]
121
        );
122
123
        return parent::delete($id);
124
    }
125
}
126
127
/**
128
 * Class SkillRelProfile.
129
 */
130
class SkillRelProfile extends Model
131
{
132
    public $columns = ['id', 'skill_id', 'profile_id'];
133
134
    /**
135
     * Constructor.
136
     */
137
    public function __construct()
138
    {
139
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
140
        $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...
141
    }
142
143
    /**
144
     * @param int $profileId
145
     *
146
     * @return array
147
     */
148
    public function getSkillsByProfile($profileId)
149
    {
150
        $profileId = (int) $profileId;
151
        $skills = $this->get_all(['where' => ['profile_id = ? ' => $profileId]]);
152
        $return = [];
153
        if (!empty($skills)) {
154
            foreach ($skills as $skill_data) {
155
                $return[] = $skill_data['skill_id'];
156
            }
157
        }
158
159
        return $return;
160
    }
161
162
    /**
163
     * This function is for getting profile info from profile_id.
164
     *
165
     * @param int $profileId
166
     *
167
     * @return array
168
     */
169
    public function getProfileInfo($profileId)
170
    {
171
        $profileId = (int) $profileId;
172
        $sql = "SELECT * FROM $this->table p
173
                INNER JOIN $this->tableProfile pr
174
                ON (pr.id = p.profile_id)
175
                WHERE p.profile_id = ".$profileId;
176
        $result = Database::query($sql);
177
        $profileData = Database::fetch_array($result, 'ASSOC');
178
179
        return $profileData;
180
    }
181
}
182
183
/**
184
 * Class SkillRelSkill.
185
 */
186
class SkillRelSkill extends Model
187
{
188
    public $columns = ['skill_id', 'parent_id', 'relation_type', 'level'];
189
190
    /**
191
     * Constructor.
192
     */
193
    public function __construct()
194
    {
195
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
196
        $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...
197
    }
198
199
    /**
200
     * Gets an element.
201
     *
202
     * @param int $id
203
     *
204
     * @return array
205
     */
206
    public function getSkillInfo($id)
207
    {
208
        $id = (int) $id;
209
210
        if (empty($id)) {
211
            return [];
212
        }
213
214
        $result = Database::select(
215
            '*',
216
            $this->table,
217
            ['where' => ['skill_id = ?' => $id]],
218
            'first'
219
        );
220
221
        return $result;
222
    }
223
224
    /**
225
     * @param int  $skillId
226
     * @param bool $add_child_info
227
     *
228
     * @return array
229
     */
230
    public function getSkillParents($skillId, $add_child_info = true)
231
    {
232
        $skillId = (int) $skillId;
233
        $sql = 'SELECT child.* FROM '.$this->table.' child
234
                LEFT JOIN '.$this->table.' parent
235
                ON child.parent_id = parent.skill_id
236
                WHERE child.skill_id = '.$skillId.' ';
237
        $result = Database::query($sql);
238
        $skill = Database::store_result($result, 'ASSOC');
239
        $skill = isset($skill[0]) ? $skill[0] : null;
240
241
        $parents = [];
242
        if (!empty($skill)) {
243
            if ($skill['parent_id'] != null) {
244
                $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

244
                /** @scrutinizer ignore-call */ 
245
                $parents = self::getSkillParents($skill['parent_id']);
Loading history...
245
            }
246
            if ($add_child_info) {
247
                $parents[] = $skill;
248
            }
249
        }
250
251
        return $parents;
252
    }
253
254
    /**
255
     * @param int $skillId
256
     *
257
     * @return array
258
     */
259
    public function getDirectParents($skillId)
260
    {
261
        $skillId = (int) $skillId;
262
        $sql = 'SELECT parent_id as skill_id 
263
                FROM '.$this->table.'
264
                WHERE skill_id = '.$skillId;
265
        $result = Database::query($sql);
266
        $skill = Database::store_result($result, 'ASSOC');
267
        $skill = isset($skill[0]) ? $skill[0] : null;
268
        $parents = [];
269
        if (!empty($skill)) {
270
            $parents[] = $skill;
271
        }
272
273
        return $parents;
274
    }
275
276
    /**
277
     * @param int  $skill_id
278
     * @param bool $load_user_data
279
     * @param bool $user_id
280
     *
281
     * @return array
282
     */
283
    public function getChildren(
284
        $skill_id,
285
        $load_user_data = false,
286
        $user_id = false,
287
        $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

287
        /** @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...
288
    ) {
289
        $skill_id = (int) $skill_id;
290
        $sql = 'SELECT parent.* FROM '.$this->tableSkill.' skill
291
                INNER JOIN '.$this->table.' parent
292
                ON parent.id = skill.id
293
                WHERE parent_id = '.$skill_id.'
294
                ORDER BY skill.name ASC';
295
        $result = Database::query($sql);
296
        $skills = Database::store_result($result, 'ASSOC');
297
298
        $skill_obj = new Skill();
299
        $skill_rel_user = new SkillRelUser();
300
301
        if ($load_user_data) {
302
            $passed_skills = $skill_rel_user->getUserSkills($user_id);
303
            $done_skills = [];
304
            foreach ($passed_skills as $done_skill) {
305
                $done_skills[] = $done_skill['skill_id'];
306
            }
307
        }
308
309
        if (!empty($skills)) {
310
            foreach ($skills as &$skill) {
311
                $skill['data'] = $skill_obj->get($skill['skill_id']);
312
                if (isset($skill['data']) && !empty($skill['data'])) {
313
                    if (!empty($done_skills)) {
314
                        $skill['data']['passed'] = 0;
315
                        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...
316
                            $skill['data']['passed'] = 1;
317
                        }
318
                    }
319
                } else {
320
                    $skill = null;
321
                }
322
            }
323
        }
324
325
        return $skills;
326
    }
327
328
    /**
329
     * @param array $params
330
     *
331
     * @return bool
332
     */
333
    public function updateBySkill($params)
334
    {
335
        $result = Database::update(
336
            $this->table,
337
            $params,
338
            ['skill_id = ? ' => $params['skill_id']]
339
        );
340
        if ($result) {
341
            return true;
342
        }
343
344
        return false;
345
    }
346
347
    /**
348
     * @param int $skill_id
349
     * @param int $parent_id
350
     *
351
     * @return bool
352
     */
353
    public function relationExists($skill_id, $parent_id)
354
    {
355
        $result = $this->find(
356
            'all',
357
            [
358
                'where' => [
359
                    'skill_id = ? AND parent_id = ?' => [
360
                        $skill_id,
361
                        $parent_id,
362
                    ],
363
                ],
364
            ]
365
        );
366
367
        if (!empty($result)) {
368
            return true;
369
        }
370
371
        return false;
372
    }
373
}
374
375
/**
376
 * Class SkillRelGradebook.
377
 */
378
class SkillRelGradebook extends Model
379
{
380
    public $columns = ['id', 'gradebook_id', 'skill_id'];
381
382
    /**
383
     * SkillRelGradebook constructor.
384
     */
385
    public function __construct()
386
    {
387
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
388
    }
389
390
    /**
391
     * @param int $gradebookId
392
     * @param int $skillId
393
     *
394
     * @return bool
395
     */
396
    public function existsGradeBookSkill($gradebookId, $skillId)
397
    {
398
        $result = $this->find(
399
            'all',
400
            [
401
                'where' => [
402
                    'gradebook_id = ? AND skill_id = ?' => [
403
                        $gradebookId,
404
                        $skillId,
405
                    ],
406
                ],
407
            ]
408
        );
409
        if (!empty($result)) {
410
            return true;
411
        }
412
413
        return false;
414
    }
415
416
    /**
417
     * Gets an element.
418
     */
419
    public function getSkillInfo($skill_id, $gradebookId)
420
    {
421
        if (empty($skill_id)) {
422
            return [];
423
        }
424
        $result = Database::select(
425
            '*',
426
            $this->table,
427
            [
428
                'where' => [
429
                    'skill_id = ? AND gradebook_id = ? ' => [
430
                        $skill_id,
431
                        $gradebookId,
432
                    ],
433
                ],
434
            ],
435
            'first'
436
        );
437
438
        return $result;
439
    }
440
441
    /**
442
     * @param int   $skill_id
443
     * @param array $gradebook_list
444
     */
445
    public function updateGradeBookListBySkill($skill_id, $gradebook_list)
446
    {
447
        $original_gradebook_list = $this->find(
448
            'all',
449
            ['where' => ['skill_id = ?' => [$skill_id]]]
450
        );
451
        $gradebooks_to_remove = [];
452
        $gradebooks_to_add = [];
453
        $original_gradebook_list_ids = [];
454
455
        if (!empty($original_gradebook_list)) {
456
            foreach ($original_gradebook_list as $gradebook) {
457
                if (!in_array($gradebook['gradebook_id'], $gradebook_list)) {
458
                    $gradebooks_to_remove[] = $gradebook['id'];
459
                }
460
            }
461
            foreach ($original_gradebook_list as $gradebook_item) {
462
                $original_gradebook_list_ids[] = $gradebook_item['gradebook_id'];
463
            }
464
        }
465
466
        if (!empty($gradebook_list)) {
467
            foreach ($gradebook_list as $gradebook_id) {
468
                if (!in_array($gradebook_id, $original_gradebook_list_ids)) {
469
                    $gradebooks_to_add[] = $gradebook_id;
470
                }
471
            }
472
        }
473
474
        if (!empty($gradebooks_to_remove)) {
475
            foreach ($gradebooks_to_remove as $id) {
476
                $this->delete($id);
477
            }
478
        }
479
480
        if (!empty($gradebooks_to_add)) {
481
            foreach ($gradebooks_to_add as $gradebook_id) {
482
                $attributes = [
483
                    'skill_id' => $skill_id,
484
                    'gradebook_id' => $gradebook_id,
485
                ];
486
                $this->save($attributes);
487
            }
488
        }
489
    }
490
491
    /**
492
     * @param array $params
493
     *
494
     * @return bool|void
495
     */
496
    public function updateBySkill($params)
497
    {
498
        $skillInfo = $this->existsGradeBookSkill(
499
            $params['gradebook_id'],
500
            $params['skill_id']
501
        );
502
503
        if ($skillInfo) {
504
            return;
505
        } else {
506
            $result = $this->save($params);
507
        }
508
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
509
            return true;
510
        }
511
512
        return false;
513
    }
514
}
515
516
/**
517
 * Class SkillRelUser.
518
 */
519
class SkillRelUser extends Model
520
{
521
    public $columns = [
522
        'id',
523
        'user_id',
524
        'skill_id',
525
        'acquired_skill_at',
526
        'assigned_by',
527
        'course_id',
528
        'session_id',
529
    ];
530
531
    /**
532
     * Constructor.
533
     */
534
    public function __construct()
535
    {
536
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
537
    }
538
539
    /**
540
     * @param array $skill_list
541
     *
542
     * @return array
543
     */
544
    public function getUserBySkills($skill_list)
545
    {
546
        $users = [];
547
        if (!empty($skill_list)) {
548
            $skill_list = array_map('intval', $skill_list);
549
            $skill_list = implode("', '", $skill_list);
550
551
            $sql = "SELECT user_id FROM {$this->table}
552
                    WHERE skill_id IN ('$skill_list') ";
553
554
            $result = Database::query($sql);
555
            $users = Database::store_result($result, 'ASSOC');
556
        }
557
558
        return $users;
559
    }
560
561
    /**
562
     * Get the achieved skills for the user.
563
     *
564
     * @param int $userId
565
     * @param int $courseId  Optional. The course id
566
     * @param int $sessionId Optional. The session id
567
     *
568
     * @return array The skill list. Otherwise return false
569
     */
570
    public function getUserSkills($userId, $courseId = 0, $sessionId = 0)
571
    {
572
        if (empty($userId)) {
573
            return [];
574
        }
575
576
        $courseId = (int) $courseId;
577
        $sessionId = $sessionId ? (int) $sessionId : null;
578
        $whereConditions = [
579
            'user_id = ? ' => (int) $userId,
580
        ];
581
582
        if ($courseId > 0) {
583
            $whereConditions['AND course_id = ? '] = $courseId;
584
            $whereConditions['AND session_id = ?'] = $sessionId;
585
        }
586
587
        $result = Database::select(
588
            'skill_id',
589
            $this->table,
590
            [
591
                'where' => $whereConditions,
592
            ],
593
            'all'
594
        );
595
596
        return $result;
597
    }
598
599
    /**
600
     * Get the relation data between user and skill.
601
     *
602
     * @param int $userId    The user id
603
     * @param int $skillId   The skill id
604
     * @param int $courseId  Optional. The course id
605
     * @param int $sessionId Optional. The session id
606
     *
607
     * @return array The relation data. Otherwise return false
608
     */
609
    public function getByUserAndSkill($userId, $skillId, $courseId = 0, $sessionId = 0)
610
    {
611
        $sql = "SELECT * FROM {$this->table} WHERE user_id = %d AND skill_id = %d ";
612
613
        if ($courseId > 0) {
614
            $sql .= "AND course_id = %d ".api_get_session_condition($sessionId, true);
615
        }
616
617
        $sql = sprintf(
618
            $sql,
619
            $userId,
620
            $skillId,
621
            $courseId
622
        );
623
624
        $result = Database::query($sql);
625
626
        return Database::fetch_assoc($result);
627
    }
628
629
    /**
630
     * Get the URL for the issue.
631
     *
632
     * @param SkillRelUserEntity $skillIssue
633
     *
634
     * @return string
635
     */
636
    public static function getIssueUrl(SkillRelUserEntity $skillIssue)
637
    {
638
        return api_get_path(WEB_PATH)."badge/{$skillIssue->getId()}";
639
    }
640
641
    /**
642
     * Get the URL for the All issues page.
643
     *
644
     * @param SkillRelUserEntity $skillIssue
645
     *
646
     * @return string
647
     */
648
    public static function getIssueUrlAll(SkillRelUserEntity $skillIssue)
649
    {
650
        return api_get_path(WEB_PATH)."skill/{$skillIssue->getSkill()->getId()}/user/{$skillIssue->getUser()->getId()}";
651
    }
652
653
    /**
654
     * Get the URL for the assertion.
655
     *
656
     * @param SkillRelUserEntity $skillIssue
657
     *
658
     * @return string
659
     */
660
    public static function getAssertionUrl(SkillRelUserEntity $skillIssue)
661
    {
662
        $url = api_get_path(WEB_CODE_PATH).'badge/assertion.php?';
663
664
        $url .= http_build_query([
665
            'user' => $skillIssue->getUser()->getId(),
666
            'skill' => $skillIssue->getSkill()->getId(),
667
            'course' => $skillIssue->getCourse() ? $skillIssue->getCourse()->getId() : 0,
668
            'session' => $skillIssue->getSession() ? $skillIssue->getSession()->getId() : 0,
669
        ]);
670
671
        return $url;
672
    }
673
}
674
675
/**
676
 * Class Skill.
677
 */
678
class Skill extends Model
679
{
680
    public $columns = [
681
        'id',
682
        'name',
683
        'description',
684
        'access_url_id',
685
        'updated_at',
686
        'short_code',
687
        'icon',
688
        'criteria',
689
    ];
690
    public $required = ['name'];
691
692
    /** Array of colours by depth, for the coffee wheel. Each depth has 4 col */
693
    /*var $colours = array(
694
      0 => array('#f9f0ab', '#ecc099', '#e098b0', '#ebe378'),
695
      1 => array('#d5dda1', '#4a5072', '#8dae43', '#72659d'),
696
      2 => array('#b28647', '#2e6093', '#393e64', '#1e8323'),
697
      3 => array('#9f6652', '#9f6652', '#9f6652', '#9f6652'),
698
      4 => array('#af643c', '#af643c', '#af643c', '#af643c'),
699
      5 => array('#72659d', '#72659d', '#72659d', '#72659d'),
700
      6 => array('#8a6e9e', '#8a6e9e', '#8a6e9e', '#8a6e9e'),
701
      7 => array('#92538c', '#92538c', '#92538c', '#92538c'),
702
      8 => array('#2e6093', '#2e6093', '#2e6093', '#2e6093'),
703
      9 => array('#3a5988', '#3a5988', '#3a5988', '#3a5988'),
704
     10 => array('#393e64', '#393e64', '#393e64', '#393e64'),
705
    );*/
706
    public function __construct()
707
    {
708
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL);
709
        $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...
710
        $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...
711
        $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...
712
        $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...
713
        $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...
714
        $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...
715
        $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...
716
    }
717
718
    /**
719
     * Gets an element.
720
     *
721
     * @param int $id
722
     *
723
     * @return array|mixed
724
     */
725
    public function get($id)
726
    {
727
        $result = parent::get($id);
728
        if (empty($result)) {
729
            return [];
730
        }
731
732
        $path = api_get_path(WEB_UPLOAD_PATH).'badges/';
733
        if (!empty($result['icon'])) {
734
            $iconSmall = sprintf(
735
                '%s-small.png',
736
                sha1($result['name'])
737
            );
738
739
            $iconBig = sprintf(
740
                '%s.png',
741
                sha1($result['name'])
742
            );
743
744
            $iconMini = $path.$iconSmall;
745
            $iconSmall = $path.$iconSmall;
746
            $iconBig = $path.$iconBig;
747
        } else {
748
            $iconMini = Display::returnIconPath('badges-default.png', ICON_SIZE_MEDIUM);
749
            $iconSmall = Display::returnIconPath('badges-default.png', ICON_SIZE_BIG);
750
            $iconBig = Display::returnIconPath('badges-default.png', ICON_SIZE_HUGE);
751
        }
752
753
        $result['icon_mini'] = $iconMini;
754
        $result['icon_small'] = $iconSmall;
755
        $result['icon_big'] = $iconBig;
756
757
        $result['img_mini'] = Display::img($iconBig, $result['name'], ['width' => ICON_SIZE_MEDIUM]);
758
        $result['img_big'] = Display::img($iconBig, $result['name']);
759
        $result['img_small'] = Display::img($iconSmall, $result['name']);
760
        $result['name'] = self::translateName($result['name']);
761
        $result['short_code'] = self::translateCode($result['short_code']);
762
763
        return $result;
764
    }
765
766
    /**
767
     * @param array  $skills
768
     * @param string $imageSize     mini|small|big
769
     * @param bool   $addDivWrapper
770
     *
771
     * @return string
772
     */
773
    public function processSkillList($skills, $imageSize = '', $addDivWrapper = true)
774
    {
775
        if (empty($skills)) {
776
            return '';
777
        }
778
779
        if (empty($imageSize)) {
780
            $imageSize = 'img_small';
781
        } else {
782
            $imageSize = "img_$imageSize";
783
        }
784
785
        $html = '';
786
        if ($addDivWrapper) {
787
            $html = '<div class="scrollbar-inner badges-sidebar">';
788
        }
789
        $html .= '<ul class="list-unstyled list-badges">';
790
        foreach ($skills as $skill) {
791
            if (isset($skill['data'])) {
792
                $skill = $skill['data'];
793
            }
794
            $html .= '<li class="thumbnail">';
795
            $item = $skill[$imageSize];
796
            $item .= '<div class="caption">
797
                        <p class="text-center">'.$skill['name'].'</p>
798
                      </div>';
799
            if (isset($skill['url'])) {
800
                $html .= Display::url($item, $skill['url'], ['target' => '_blank']);
801
            } else {
802
                $html .= $item;
803
            }
804
            $html .= '</li>';
805
        }
806
        $html .= '</ul>';
807
808
        if ($addDivWrapper) {
809
            $html .= '</div>';
810
        }
811
812
        return $html;
813
    }
814
815
    /**
816
     * @param $skills
817
     * @param string $imageSize mini|small|big
818
     * @param string $style
819
     * @param bool   $showBadge
820
     * @param bool   $showTitle
821
     *
822
     * @return string
823
     */
824
    public function processSkillListSimple($skills, $imageSize = '', $style = '', $showBadge = true, $showTitle = true)
825
    {
826
        if (empty($skills)) {
827
            return '';
828
        }
829
830
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
831
832
        if (empty($imageSize)) {
833
            $imageSize = 'img_small';
834
        } else {
835
            $imageSize = "img_$imageSize";
836
        }
837
838
        $html = '';
839
        foreach ($skills as $skill) {
840
            if (isset($skill['data'])) {
841
                $skill = $skill['data'];
842
            }
843
844
            $item = '';
845
            if ($showBadge) {
846
                $item = '<div class="item">'.$skill[$imageSize].'</div>';
847
            }
848
849
            $name = '<div class="caption">'.$skill['name'].'</div>';
850
            if (!empty($skill['short_code'])) {
851
                $name = $skill['short_code'];
852
            }
853
854
            if (!$isHierarchicalTable) {
855
                //$item .= '<br />';
856
            }
857
858
            if ($showTitle) {
859
                $item .= $name;
860
            }
861
862
            if (isset($skill['url'])) {
863
                $html .= Display::url($item, $skill['url'], ['target' => '_blank', 'style' => $style]);
864
            } else {
865
                $html .= Display::url($item, '#', ['target' => '_blank', 'style' => $style]);
866
            }
867
        }
868
869
        return $html;
870
    }
871
872
    /**
873
     * @param int $id
874
     *
875
     * @return array
876
     */
877
    public function getSkillInfo($id)
878
    {
879
        $skillRelSkill = new SkillRelSkill();
880
        $skillInfo = $this->get($id);
881
        if (!empty($skillInfo)) {
882
            $skillInfo['extra'] = $skillRelSkill->getSkillInfo($id);
883
            $skillInfo['gradebooks'] = $this->getGradebooksBySkill($id);
884
        }
885
886
        return $skillInfo;
887
    }
888
889
    /**
890
     * @param array $skill_list
891
     *
892
     * @return array
893
     */
894
    public function getSkillsInfo($skill_list)
895
    {
896
        $skill_list = array_map('intval', $skill_list);
897
        $skill_list = implode("', '", $skill_list);
898
899
        $sql = "SELECT * FROM {$this->table}
900
                WHERE id IN ('$skill_list') ";
901
902
        $result = Database::query($sql);
903
        $skills = Database::store_result($result, 'ASSOC');
904
905
        foreach ($skills as &$skill) {
906
            if (!$skill['icon']) {
907
                continue;
908
            }
909
910
            $skill['icon_small'] = sprintf(
911
                'badges/%s-small.png',
912
                sha1($skill['name'])
913
            );
914
            $skill['name'] = self::translateName($skill['name']);
915
            $skill['short_code'] = self::translateCode($skill['short_code']);
916
        }
917
918
        return $skills;
919
    }
920
921
    /**
922
     * @param bool $load_user_data
923
     * @param bool $user_id
924
     * @param int  $id
925
     * @param int  $parent_id
926
     *
927
     * @return array
928
     */
929
    public function get_all(
930
        $load_user_data = false,
931
        $user_id = false,
932
        $id = null,
933
        $parent_id = null
934
    ) {
935
        $id_condition = '';
936
        if (!empty($id)) {
937
            $id = (int) $id;
938
            $id_condition = " WHERE s.id = $id";
939
        }
940
941
        if (!empty($parent_id)) {
942
            $parent_id = (int) $parent_id;
943
            if (empty($id_condition)) {
944
                $id_condition = " WHERE ss.parent_id = $parent_id";
945
            } else {
946
                $id_condition = " AND ss.parent_id = $parent_id";
947
            }
948
        }
949
950
        $sql = "SELECT
951
                    s.id,
952
                    s.name,
953
                    s.description,
954
                    ss.parent_id,
955
                    ss.relation_type,
956
                    s.icon,
957
                    s.short_code,
958
                    s.status
959
                FROM {$this->table} s
960
                INNER JOIN {$this->table_skill_rel_skill} ss
961
                ON (s.id = ss.skill_id) $id_condition
962
                ORDER BY ss.id, ss.parent_id";
963
964
        $result = Database::query($sql);
965
        $skills = [];
966
        $webPath = api_get_path(WEB_UPLOAD_PATH);
967
        if (Database::num_rows($result)) {
968
            while ($row = Database::fetch_array($result, 'ASSOC')) {
969
                $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

969
                /** @scrutinizer ignore-call */ 
970
                $skillInfo = self::get($row['id']);
Loading history...
970
971
                $row['img_mini'] = $skillInfo['img_mini'];
972
                $row['img_big'] = $skillInfo['img_big'];
973
                $row['img_small'] = $skillInfo['img_small'];
974
975
                $row['name'] = self::translateName($row['name']);
976
                $row['short_code'] = self::translateCode($row['short_code']);
977
                $skillRelSkill = new SkillRelSkill();
978
                $parents = $skillRelSkill->getSkillParents($row['id']);
979
                $row['level'] = count($parents) - 1;
980
                $row['gradebooks'] = $this->getGradebooksBySkill($row['id']);
981
                $skills[$row['id']] = $row;
982
            }
983
        }
984
985
        // Load all children of the parent_id
986
        if (!empty($skills) && !empty($parent_id)) {
987
            foreach ($skills as $skill) {
988
                $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

988
                /** @scrutinizer ignore-call */ 
989
                $children = self::get_all($load_user_data, $user_id, $id, $skill['id']);
Loading history...
989
                if (!empty($children)) {
990
                    //$skills = array_merge($skills, $children);
991
                    $skills = $skills + $children;
992
                }
993
            }
994
        }
995
996
        return $skills;
997
    }
998
999
    /**
1000
     * @param int $skill_id
1001
     *
1002
     * @return array|resource
1003
     */
1004
    public function getGradebooksBySkill($skill_id)
1005
    {
1006
        $skill_id = (int) $skill_id;
1007
        $sql = "SELECT g.* FROM {$this->table_gradebook} g
1008
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1009
                ON g.id = sg.gradebook_id
1010
                WHERE sg.skill_id = $skill_id";
1011
        $result = Database::query($sql);
1012
        $result = Database::store_result($result, 'ASSOC');
1013
1014
        return $result;
1015
    }
1016
1017
    /**
1018
     * Get one level children.
1019
     *
1020
     * @param int  $skill_id
1021
     * @param bool $load_user_data
1022
     *
1023
     * @return array
1024
     */
1025
    public function getChildren($skill_id, $load_user_data = false)
1026
    {
1027
        $skillRelSkill = new SkillRelSkill();
1028
        if ($load_user_data) {
1029
            $user_id = api_get_user_id();
1030
            $skills = $skillRelSkill->getChildren($skill_id, true, $user_id);
1031
        } else {
1032
            $skills = $skillRelSkill->getChildren($skill_id);
1033
        }
1034
1035
        return $skills;
1036
    }
1037
1038
    /**
1039
     * Get all children of the current node (recursive).
1040
     *
1041
     * @param int $skillId
1042
     *
1043
     * @return array
1044
     */
1045
    public function getAllChildren($skillId)
1046
    {
1047
        $skillRelSkill = new SkillRelSkill();
1048
        $children = $skillRelSkill->getChildren($skillId);
1049
        foreach ($children as $child) {
1050
            $subChildren = $this->getAllChildren($child['id']);
1051
        }
1052
1053
        if (!empty($subChildren)) {
1054
            $children = array_merge($children, $subChildren);
1055
        }
1056
1057
        return $children;
1058
    }
1059
1060
    /**
1061
     * Gets all parents from from the wanted skill.
1062
     */
1063
    public function get_parents($skillId)
1064
    {
1065
        $skillRelSkill = new SkillRelSkill();
1066
        $skills = $skillRelSkill->getSkillParents($skillId, true);
1067
        foreach ($skills as &$skill) {
1068
            $skill['data'] = $this->get($skill['skill_id']);
1069
        }
1070
1071
        return $skills;
1072
    }
1073
1074
    /**
1075
     * All direct parents.
1076
     *
1077
     * @param int $skillId
1078
     *
1079
     * @return array
1080
     */
1081
    public function getDirectParents($skillId)
1082
    {
1083
        $skillRelSkill = new SkillRelSkill();
1084
        $skills = $skillRelSkill->getDirectParents($skillId, true);
1085
        if (!empty($skills)) {
1086
            foreach ($skills as &$skill) {
1087
                $skillData = $this->get($skill['skill_id']);
1088
                if (empty($skillData)) {
1089
                    continue;
1090
                }
1091
                $skill['data'] = $skillData;
1092
                $skill_info2 = $skillRelSkill->getSkillInfo($skill['skill_id']);
1093
                $parentId = isset($skill_info2['parent_id']) ? isset($skill_info2['parent_id']) : 0;
1094
                $skill['data']['parent_id'] = $parentId;
1095
            }
1096
1097
            return $skills;
1098
        }
1099
1100
        return [];
1101
    }
1102
1103
    /**
1104
     * Adds a new skill.
1105
     *
1106
     * @param array $params
1107
     *
1108
     * @return bool|null
1109
     */
1110
    public function add($params)
1111
    {
1112
        if (!isset($params['parent_id'])) {
1113
            $params['parent_id'] = 1;
1114
        }
1115
1116
        if (!is_array($params['parent_id'])) {
1117
            $params['parent_id'] = [$params['parent_id']];
1118
        }
1119
1120
        $skillRelSkill = new SkillRelSkill();
1121
        $skillRelGradebook = new SkillRelGradebook();
1122
1123
        // Saving name, description
1124
        $params['access_url_id'] = api_get_current_access_url_id();
1125
        $params['icon'] = '';
1126
1127
        $skill_id = $this->save($params);
1128
        if ($skill_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $skill_id of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1129
            // Saving skill_rel_skill (parent_id, relation_type)
1130
            foreach ($params['parent_id'] as $parent_id) {
1131
                $relation_exists = $skillRelSkill->relationExists($skill_id, $parent_id);
1132
                if (!$relation_exists) {
1133
                    $attributes = [
1134
                        'skill_id' => $skill_id,
1135
                        'parent_id' => $parent_id,
1136
                        'relation_type' => isset($params['relation_type']) ? $params['relation_type'] : 0,
1137
                        'level' => isset($params['level']) ? $params['level'] : 0,
1138
                    ];
1139
                    $skillRelSkill->save($attributes);
1140
                }
1141
            }
1142
1143
            if (!empty($params['gradebook_id'])) {
1144
                foreach ($params['gradebook_id'] as $gradebook_id) {
1145
                    $attributes = [];
1146
                    $attributes['gradebook_id'] = $gradebook_id;
1147
                    $attributes['skill_id'] = $skill_id;
1148
                    $skillRelGradebook->save($attributes);
1149
                }
1150
            }
1151
1152
            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 boolean|null.
Loading history...
1153
        }
1154
1155
        return null;
1156
    }
1157
1158
    /**
1159
     * @param int      $userId
1160
     * @param Category $category
1161
     * @param int      $courseId
1162
     * @param int      $sessionId
1163
     *
1164
     * @return bool
1165
     */
1166
    public function addSkillToUser(
1167
        $userId,
1168
        $category,
1169
        $courseId,
1170
        $sessionId
1171
    ) {
1172
        $skill_gradebook = new SkillRelGradebook();
1173
        $skill_rel_user = new SkillRelUser();
1174
1175
        if (empty($category)) {
1176
            return false;
1177
        }
1178
1179
        // Load subcategories
1180
        if (empty($category->get_parent_id())) {
1181
            $subCategories = $category->get_subcategories(
1182
                $userId,
1183
                $category->get_course_code(),
1184
                $category->get_session_id()
1185
            );
1186
            if (!empty($subCategories)) {
1187
                /** @var Category $subCategory */
1188
                foreach ($subCategories as $subCategory) {
1189
                    $this->addSkillToUser($userId, $subCategory, $courseId, $sessionId);
1190
                }
1191
            }
1192
        }
1193
1194
        $gradebookId = $category->get_id();
1195
        $skill_gradebooks = $skill_gradebook->get_all(['where' => ['gradebook_id = ?' => $gradebookId]]);
1196
1197
        if (!empty($skill_gradebooks)) {
1198
            foreach ($skill_gradebooks as $skill_gradebook) {
1199
                $hasSkill = $this->userHasSkill(
1200
                    $userId,
1201
                    $skill_gradebook['skill_id'],
1202
                    $courseId,
1203
                    $sessionId
1204
                );
1205
1206
                if (!$hasSkill) {
1207
                    $params = [
1208
                        'user_id' => $userId,
1209
                        'skill_id' => $skill_gradebook['skill_id'],
1210
                        'acquired_skill_at' => api_get_utc_datetime(),
1211
                        'course_id' => (int) $courseId,
1212
                        'session_id' => $sessionId ? (int) $sessionId : null,
1213
                    ];
1214
1215
                    $skill_rel_user->save($params);
1216
                }
1217
            }
1218
        }
1219
1220
        return true;
1221
    }
1222
1223
    /* Deletes a skill */
1224
    public function delete($skill_id)
1225
    {
1226
        /*$params = array('skill_id' => $skill_id);
1227
1228
        $skillRelSkill     = new SkillRelSkill();
1229
        $skills = $skillRelSkill->get_all(array('where'=>array('skill_id = ?' =>$skill_id)));
1230
1231
        $skill_rel_profile     = new SkillRelProfile();
1232
        $skillRelGradebook = new SkillRelGradebook();
1233
        $skill_rel_user     = new SkillRelUser();
1234
1235
        $this->delete($skill_id);
1236
1237
        $skillRelGradebook->delete($params);*/
1238
    }
1239
1240
    /**
1241
     * @param array $params
1242
     */
1243
    public function edit($params)
1244
    {
1245
        if (!isset($params['parent_id'])) {
1246
            $params['parent_id'] = 1;
1247
        }
1248
1249
        $params['gradebook_id'] = isset($params['gradebook_id']) ? $params['gradebook_id'] : [];
1250
1251
        $skillRelSkill = new SkillRelSkill();
1252
        $skillRelGradebook = new SkillRelGradebook();
1253
1254
        // Saving name, description
1255
        $this->update($params);
1256
        $skillId = $params['id'];
1257
1258
        if ($skillId) {
1259
            // Saving skill_rel_skill (parent_id, relation_type)
1260
            if (!is_array($params['parent_id'])) {
1261
                $params['parent_id'] = [$params['parent_id']];
1262
            }
1263
1264
            // Cannot change parent of root
1265
            if ($skillId == 1) {
1266
                $params['parent_id'] = 0;
1267
            }
1268
1269
            foreach ($params['parent_id'] as $parent_id) {
1270
                $relation_exists = $skillRelSkill->relationExists($skillId, $parent_id);
1271
                if (!$relation_exists) {
1272
                    $attributes = [
1273
                        'skill_id' => $skillId,
1274
                        'parent_id' => $parent_id,
1275
                        'relation_type' => $params['relation_type'],
1276
                        //'level'         => $params['level'],
1277
                    ];
1278
                    $skillRelSkill->updateBySkill($attributes);
1279
                }
1280
            }
1281
1282
            $skillRelGradebook->updateGradeBookListBySkill(
1283
                $skillId,
1284
                $params['gradebook_id']
1285
            );
1286
1287
            return $skillId;
1288
        }
1289
1290
        return null;
1291
    }
1292
1293
    /**
1294
     * Get user's skills.
1295
     *
1296
     * @param int  $userId
1297
     * @param bool $getSkillData
1298
     * @param int  $courseId
1299
     * @param int  $sessionId
1300
     *
1301
     * @return array
1302
     */
1303
    public function getUserSkills($userId, $getSkillData = false, $courseId = 0, $sessionId = 0)
1304
    {
1305
        $userId = (int) $userId;
1306
        $courseId = (int) $courseId;
1307
        $sessionId = (int) $sessionId;
1308
1309
        $courseCondition = '';
1310
        if (!empty($courseId)) {
1311
            $courseCondition = " AND course_id = $courseId ";
1312
        }
1313
1314
        $sessionCondition = '';
1315
        if (!empty($sessionId)) {
1316
            $sessionCondition = " AND course_id = $sessionId ";
1317
        }
1318
1319
        $sql = 'SELECT DISTINCT
1320
                    s.id,
1321
                    s.name,
1322
                    s.icon,
1323
                    u.id as issue,
1324
                    u.acquired_skill_at,
1325
                    u.course_id
1326
                FROM '.$this->table_skill_rel_user.' u
1327
                INNER JOIN '.$this->table.' s
1328
                ON u.skill_id = s.id
1329
                WHERE
1330
                    user_id = '.$userId.' '.$sessionCondition.' '.$courseCondition;
1331
1332
        $result = Database::query($sql);
1333
        $skills = Database::store_result($result, 'ASSOC');
1334
        $skillList = [];
1335
        if (!empty($skills)) {
1336
            foreach ($skills as $skill) {
1337
                if ($getSkillData) {
1338
                    $skillData = $this->get($skill['id']);
1339
                    $skillData['url'] = api_get_path(WEB_PATH).'badge/'.$skill['id'].'/user/'.$userId;
1340
                    $skillList[$skill['id']] = array_merge($skill, $skillData);
1341
                } else {
1342
                    $skillList[$skill['id']] = $skill['id'];
1343
                }
1344
            }
1345
        }
1346
1347
        return $skillList;
1348
    }
1349
1350
    /**
1351
     * @param Vertex $vertex
1352
     * @param array  $skills
1353
     * @param int    $level
1354
     *
1355
     * @return string
1356
     */
1357
    public function processVertex(Vertex $vertex, $skills = [], $level = 0)
1358
    {
1359
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
1360
        $subTable = '';
1361
        if ($vertex->getVerticesEdgeTo()->count() > 0) {
1362
            if ($isHierarchicalTable) {
1363
                $subTable .= '<ul>';
1364
            }
1365
            foreach ($vertex->getVerticesEdgeTo() as $subVertex) {
1366
                $data = $subVertex->getAttribute('graphviz.data');
1367
                $passed = in_array($data['id'], array_keys($skills));
1368
                $transparency = '';
1369
                if ($passed === false) {
1370
                    // @todo use css class
1371
                    $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1372
                }
1373
1374
                if ($isHierarchicalTable) {
1375
                    $label = $this->processSkillListSimple([$data], 'mini', $transparency);
1376
                    $subTable .= '<li>'.$label;
1377
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
1378
                    $subTable .= '</li>';
1379
                } else {
1380
                    $imageSize = 'mini';
1381
                    if ($level == 2) {
1382
                        $imageSize = 'small';
1383
                    }
1384
                    $showTitle = true;
1385
                    if ($level > 2) {
1386
                        $showTitle = false;
1387
                    }
1388
1389
                    $label = $this->processSkillListSimple([$data], $imageSize, $transparency, true, $showTitle);
1390
                    $subTable .= '<div class="thumbnail" style="float:left; margin-right:5px; ">';
1391
                    $subTable .= '<div style="'.$transparency.'">';
1392
1393
                    $subTable .= '<div style="text-align: center">';
1394
                    $subTable .= $label;
1395
                    $subTable .= '</div>';
1396
1397
                    $subTable .= '</div>';
1398
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
1399
                    $subTable .= '</div>';
1400
                }
1401
            }
1402
1403
            if ($isHierarchicalTable) {
1404
                $subTable .= '</ul>';
1405
            }
1406
        }
1407
1408
        return $subTable;
1409
    }
1410
1411
    /**
1412
     * @param int  $userId
1413
     * @param int  $courseId
1414
     * @param int  $sessionId
1415
     * @param bool $addTitle
1416
     *
1417
     * @return array
1418
     */
1419
    public function getUserSkillsTable($userId, $courseId = 0, $sessionId = 0, $addTitle = true)
1420
    {
1421
        $skills = $this->getUserSkills($userId, true, $courseId, $sessionId);
1422
        $courseTempList = [];
1423
        $tableRows = [];
1424
        $skillParents = [];
1425
        foreach ($skills as $resultData) {
1426
            $parents = $this->get_parents($resultData['id']);
1427
            foreach ($parents as $parentData) {
1428
                $parentData['passed'] = in_array($parentData['id'], array_keys($skills));
1429
                if ($parentData['passed'] && isset($skills[$parentData['id']]['url'])) {
1430
                    $parentData['data']['url'] = $skills[$parentData['id']]['url'];
1431
                }
1432
                $skillParents[$resultData['id']][$parentData['id']] = $parentData;
1433
            }
1434
        }
1435
1436
        foreach ($skills as $resultData) {
1437
            $courseId = $resultData['course_id'];
1438
            if (!empty($courseId)) {
1439
                if (isset($courseTempList[$courseId])) {
1440
                    $courseInfo = $courseTempList[$courseId];
1441
                } else {
1442
                    $courseInfo = api_get_course_info_by_id($courseId);
1443
                    $courseTempList[$courseId] = $courseInfo;
1444
                }
1445
            }
1446
            $tableRow = [
1447
                'skill_badge' => $resultData['img_small'],
1448
                'skill_name' => self::translateName($resultData['name']),
1449
                'short_code' => $resultData['short_code'],
1450
                'skill_url' => $resultData['url'],
1451
                'achieved_at' => api_get_local_time($resultData['acquired_skill_at']),
1452
                'course_image' => '',
1453
                'course_name' => '',
1454
            ];
1455
1456
            if (!empty($courseInfo)) {
1457
                $tableRow['course_image'] = $courseInfo['course_image'];
1458
                $tableRow['course_name'] = $courseInfo['title'];
1459
            }
1460
            $tableRows[] = $tableRow;
1461
        }
1462
1463
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
1464
        $allowLevels = api_get_configuration_value('skill_levels_names');
1465
1466
        $tableResult = '<div id="skillList">';
1467
        if ($isHierarchicalTable) {
1468
            $tableResult = '<div class="table-responsive">';
1469
        }
1470
1471
        if ($addTitle) {
1472
            $tableResult .= '<h3 class="section-title">'.get_lang('AchievedSkills').'</h3>
1473
                    <div class="skills-badges">
1474
                   ';
1475
        }
1476
1477
        if (!empty($skillParents)) {
1478
            if (empty($allowLevels)) {
1479
                $tableResult .= $this->processSkillListSimple($skills);
1480
            } else {
1481
                $graph = new Graph();
1482
                $graph->setAttribute('graphviz.graph.rankdir', 'LR');
1483
                foreach ($skillParents as $skillId => $parentList) {
1484
                    $old = null;
1485
                    foreach ($parentList as $parent) {
1486
                        if ($graph->hasVertex($parent['id'])) {
1487
                            $current = $graph->getVertex($parent['id']);
1488
                        } else {
1489
                            $current = $graph->createVertex($parent['id']);
1490
                            $current->setAttribute('graphviz.data', $parent['data']);
1491
                        }
1492
1493
                        if (!empty($old)) {
1494
                            if ($graph->hasVertex($old['id'])) {
1495
                                $nextVertex = $graph->getVertex($old['id']);
1496
                            } else {
1497
                                $nextVertex = $graph->createVertex($old['id']);
1498
                                $nextVertex->setAttribute('graphviz.data', $old['data']);
1499
                            }
1500
1501
                            if (!$nextVertex->hasEdgeTo($current)) {
1502
                                $nextVertex->createEdgeTo($current);
1503
                            }
1504
                        }
1505
                        $old = $parent;
1506
                    }
1507
                }
1508
1509
                if ($isHierarchicalTable) {
1510
                    $table = '<table class ="table table-bordered">';
1511
                    // Getting "root" vertex
1512
                    $root = $graph->getVertex(1);
1513
                    $table .= '<tr>';
1514
                    /** @var Vertex $vertex */
1515
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
1516
                        $data = $vertex->getAttribute('graphviz.data');
1517
1518
                        $passed = in_array($data['id'], array_keys($skills));
1519
                        $transparency = '';
1520
                        if ($passed === false) {
1521
                            // @todo use a css class
1522
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1523
                        }
1524
1525
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency);
1526
                        $table .= '<td >';
1527
1528
                        $table .= '<div class="skills_chart"> <ul><li>'.$label;
1529
                        $table .= $this->processVertex($vertex, $skills);
1530
                        $table .= '</ul></li></div>';
1531
                        $table .= '</td>';
1532
                    }
1533
                    $table .= '</tr></table>';
1534
                } else {
1535
                    // Getting "root" vertex
1536
                    $root = $graph->getVertex(1);
1537
                    $table = '';
1538
                    /** @var Vertex $vertex */
1539
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
1540
                        $data = $vertex->getAttribute('graphviz.data');
1541
1542
                        $passed = in_array($data['id'], array_keys($skills));
1543
                        $transparency = '';
1544
                        if ($passed === false) {
1545
                            // @todo use a css class
1546
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1547
                        }
1548
1549
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency, false);
1550
1551
                        $skillTable = $this->processVertex($vertex, $skills, 2);
1552
                        $table .= "<h3>$label</h3>";
1553
1554
                        if (!empty($skillTable)) {
1555
                            $table .= '<table class ="table table-bordered">';
1556
                            $table .= '<tr>';
1557
                            $table .= '<td>';
1558
                            $table .= '<div>';
1559
                            $table .= $skillTable;
1560
                            $table .= '</div>';
1561
                            $table .= '</td>';
1562
                            $table .= '</tr></table>';
1563
                        }
1564
                    }
1565
                }
1566
1567
                $tableResult .= $table;
1568
            }
1569
        } else {
1570
            $tableResult .= get_lang('WithoutAchievedSkills');
1571
        }
1572
1573
        if ($addTitle) {
1574
            $tableResult .= '</div>';
1575
        }
1576
        $tableResult .= '</div>';
1577
1578
        return [
1579
            'skills' => $tableRows,
1580
            'table' => $tableResult,
1581
        ];
1582
    }
1583
1584
    /**
1585
     * @param int  $user_id
1586
     * @param int  $skill_id
1587
     * @param bool $return_flat_array
1588
     * @param bool $add_root
1589
     *
1590
     * @return array|null
1591
     */
1592
    public function getSkillsTree(
1593
        $user_id = null,
1594
        $skill_id = null,
1595
        $return_flat_array = false,
1596
        $add_root = false
1597
    ) {
1598
        if ($skill_id == 1) {
1599
            $skill_id = 0;
1600
        }
1601
        if (isset($user_id) && !empty($user_id)) {
1602
            $skills = $this->get_all(true, $user_id, null, $skill_id);
1603
        } else {
1604
            $skills = $this->get_all(false, false, null, $skill_id);
1605
        }
1606
1607
        $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...
1608
1609
        // Show 1 item
1610
        if (!empty($skill_id)) {
1611
            if ($add_root) {
1612
                if (!empty($skill_id)) {
1613
                    // Default root node
1614
                    $skills[1] = [
1615
                        'id' => '1',
1616
                        'name' => get_lang('Root'),
1617
                        'parent_id' => '0',
1618
                        'status' => 1,
1619
                    ];
1620
                    $skillInfo = $this->getSkillInfo($skill_id);
1621
1622
                    // 2nd node
1623
                    $skills[$skill_id] = $skillInfo;
1624
                    // Uncomment code below to hide the searched skill
1625
                    $skills[$skill_id]['data']['parent_id'] = $skillInfo['extra']['parent_id'];
1626
                    $skills[$skill_id]['parent_id'] = 1;
1627
                }
1628
            }
1629
        }
1630
1631
        $refs = [];
1632
        $skills_tree = null;
1633
1634
        // Create references for all nodes
1635
        $flat_array = [];
1636
        $family = [];
1637
        if (!empty($skills)) {
1638
            foreach ($skills as &$skill) {
1639
                if ($skill['parent_id'] == 0) {
1640
                    $skill['parent_id'] = 'root';
1641
                }
1642
1643
                // because except main keys (id, name, children) others keys
1644
                // are not saved while in the space tree
1645
                $skill['data'] = ['parent_id' => $skill['parent_id']];
1646
1647
                // If a short code was defined, send the short code to replace
1648
                // skill name (to shorten the text in the wheel)
1649
                if (!empty($skill['short_code']) &&
1650
                    api_get_setting('show_full_skill_name_on_skill_wheel') === 'false'
1651
                ) {
1652
                    $skill['data']['short_code'] = $skill['short_code'];
1653
                }
1654
1655
                $skill['data']['name'] = $skill['name'];
1656
                $skill['data']['status'] = $skill['status'];
1657
1658
                // In order to paint all members of a family with the same color
1659
                if (empty($skill_id)) {
1660
                    if ($skill['parent_id'] == 1) {
1661
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1662
                    }
1663
                } else {
1664
                    if ($skill['parent_id'] == $skill_id) {
1665
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1666
                    }
1667
                    /*if ($skill_id == $skill['id']) {
1668
                        $skill['parent_id'] = 1;
1669
                    }*/
1670
                }
1671
1672
                if (!isset($skill['data']['real_parent_id'])) {
1673
                    $skill['data']['real_parent_id'] = $skill['parent_id'];
1674
                }
1675
1676
                // User achieved the skill (depends in the gradebook with certification)
1677
                $skill['data']['achieved'] = false;
1678
                if ($user_id) {
1679
                    $skill['data']['achieved'] = $this->userHasSkill(
1680
                        $user_id,
1681
                        $skill['id']
1682
                    );
1683
                }
1684
1685
                // Check if the skill has related gradebooks
1686
                $skill['data']['skill_has_gradebook'] = false;
1687
                if (isset($skill['gradebooks']) && !empty($skill['gradebooks'])) {
1688
                    $skill['data']['skill_has_gradebook'] = true;
1689
                }
1690
                $refs[$skill['id']] = &$skill;
1691
                $flat_array[$skill['id']] = &$skill;
1692
            }
1693
1694
            // Checking family value
1695
1696
            $family_id = 1;
1697
            $new_family_array = [];
1698
            foreach ($family as $main_family_id => $family_items) {
1699
                if (!empty($family_items)) {
1700
                    foreach ($family_items as $item) {
1701
                        $new_family_array[$item['id']] = $family_id;
1702
                    }
1703
                }
1704
                $new_family_array[$main_family_id] = $family_id;
1705
                $family_id++;
1706
            }
1707
1708
            if (empty($original_skill)) {
1709
                $refs['root']['children'][0] = $skills[1];
1710
                $skills[$skill_id]['data']['family_id'] = 1;
1711
                $refs['root']['children'][0]['children'][0] = $skills[$skill_id];
1712
                $flat_array[$skill_id] = $skills[$skill_id];
1713
            } else {
1714
                // Moving node to the children index of their parents
1715
                foreach ($skills as $my_skill_id => &$skill) {
1716
                    if (isset($new_family_array[$skill['id']])) {
1717
                        $skill['data']['family_id'] = $new_family_array[$skill['id']];
1718
                    }
1719
                    $refs[$skill['parent_id']]['children'][] = &$skill;
1720
                    $flat_array[$my_skill_id] = $skill;
1721
                }
1722
            }
1723
1724
            $skills_tree = [
1725
                'name' => get_lang('SkillRootName'),
1726
                'id' => 'root',
1727
                'children' => $refs['root']['children'],
1728
                'data' => [],
1729
            ];
1730
        }
1731
1732
        if ($return_flat_array) {
1733
            return $flat_array;
1734
        }
1735
        unset($skills);
1736
1737
        return $skills_tree;
1738
    }
1739
1740
    /**
1741
     * Get skills tree as a simplified JSON structure.
1742
     *
1743
     * @param int user id
1744
     * @param int skill id
1745
     * @param bool return a flat array or not
1746
     * @param int depth of the skills
1747
     *
1748
     * @return string json
1749
     */
1750
    public function getSkillsTreeToJson(
1751
        $user_id = null,
1752
        $skill_id = null,
1753
        $return_flat_array = false,
1754
        $main_depth = 2
1755
    ) {
1756
        $tree = $this->getSkillsTree(
1757
            $user_id,
1758
            $skill_id,
1759
            $return_flat_array,
1760
            true
1761
        );
1762
        $simple_tree = [];
1763
        if (!empty($tree['children'])) {
1764
            foreach ($tree['children'] as $element) {
1765
                $children = [];
1766
                if (isset($element['children'])) {
1767
                    $children = $this->getSkillToJson($element['children'], 1, $main_depth);
1768
                }
1769
                $simple_tree[] = [
1770
                    'name' => $element['name'],
1771
                    'children' => $children,
1772
                ];
1773
            }
1774
        }
1775
1776
        return json_encode($simple_tree[0]['children']);
1777
    }
1778
1779
    /**
1780
     * Get JSON element.
1781
     *
1782
     * @param array $subtree
1783
     * @param int   $depth
1784
     * @param int   $max_depth
1785
     *
1786
     * @return array|null
1787
     */
1788
    public function getSkillToJson($subtree, $depth = 1, $max_depth = 2)
1789
    {
1790
        $simple_sub_tree = [];
1791
        if (is_array($subtree)) {
1792
            $counter = 1;
1793
            foreach ($subtree as $elem) {
1794
                $tmp = [];
1795
                $tmp['name'] = $elem['name'];
1796
                $tmp['id'] = $elem['id'];
1797
                $tmp['isSearched'] = self::isSearched($elem['id']);
1798
1799
                if (isset($elem['children']) && is_array($elem['children'])) {
1800
                    $tmp['children'] = $this->getSkillToJson(
1801
                        $elem['children'],
1802
                        $depth + 1,
1803
                        $max_depth
1804
                    );
1805
                }
1806
1807
                if ($depth > $max_depth) {
1808
                    continue;
1809
                }
1810
1811
                $tmp['depth'] = $depth;
1812
                $tmp['counter'] = $counter;
1813
                $counter++;
1814
1815
                if (isset($elem['data']) && is_array($elem['data'])) {
1816
                    foreach ($elem['data'] as $key => $item) {
1817
                        $tmp[$key] = $item;
1818
                    }
1819
                }
1820
                $simple_sub_tree[] = $tmp;
1821
            }
1822
1823
            return $simple_sub_tree;
1824
        }
1825
1826
        return null;
1827
    }
1828
1829
    /**
1830
     * @param int $user_id
1831
     *
1832
     * @return bool
1833
     */
1834
    public function getUserSkillRanking($user_id)
1835
    {
1836
        $user_id = (int) $user_id;
1837
        $sql = "SELECT count(skill_id) count 
1838
                FROM {$this->table} s
1839
                INNER JOIN {$this->table_skill_rel_user} su
1840
                ON (s.id = su.skill_id)
1841
                WHERE user_id = $user_id";
1842
        $result = Database::query($sql);
1843
        if (Database::num_rows($result)) {
1844
            $result = Database::fetch_row($result);
1845
1846
            return $result[0];
1847
        }
1848
1849
        return false;
1850
    }
1851
1852
    /**
1853
     * @param $start
1854
     * @param $limit
1855
     * @param $sidx
1856
     * @param $sord
1857
     * @param $where_condition
1858
     *
1859
     * @return array
1860
     */
1861
    public function getUserListSkillRanking(
1862
        $start,
1863
        $limit,
1864
        $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

1864
        /** @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...
1865
        $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

1865
        /** @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...
1866
        $where_condition
1867
    ) {
1868
        $start = (int) $start;
1869
        $limit = (int) $limit;
1870
1871
        /*  ORDER BY $sidx $sord */
1872
        $sql = "SELECT *, @rownum:=@rownum+1 rank FROM (
1873
                    SELECT u.user_id, firstname, lastname, count(username) skills_acquired
1874
                    FROM {$this->table} s INNER JOIN {$this->table_skill_rel_user} su ON (s.id = su.skill_id)
1875
                    INNER JOIN {$this->table_user} u ON u.user_id = su.user_id, (SELECT @rownum:=0) r
1876
                    WHERE 1=1 $where_condition
1877
                    GROUP BY username
1878
                    ORDER BY skills_acquired desc
1879
                    LIMIT $start , $limit)  AS T1, (SELECT @rownum:=0) r";
1880
        $result = Database::query($sql);
1881
        if (Database::num_rows($result)) {
1882
            return Database::store_result($result, 'ASSOC');
1883
        }
1884
1885
        return [];
1886
    }
1887
1888
    /**
1889
     * @return int
1890
     */
1891
    public function getUserListSkillRankingCount()
1892
    {
1893
        $sql = "SELECT count(*) FROM (
1894
                    SELECT count(distinct 1)
1895
                    FROM {$this->table} s
1896
                    INNER JOIN {$this->table_skill_rel_user} su
1897
                    ON (s.id = su.skill_id)
1898
                    INNER JOIN {$this->table_user} u
1899
                    ON u.user_id = su.user_id
1900
                    GROUP BY username
1901
                 ) as T1";
1902
        $result = Database::query($sql);
1903
        if (Database::num_rows($result)) {
1904
            $result = Database::fetch_row($result);
1905
1906
            return $result[0];
1907
        }
1908
1909
        return 0;
1910
    }
1911
1912
    /**
1913
     * @param string $courseCode
1914
     *
1915
     * @return int
1916
     */
1917
    public function getCountSkillsByCourse($courseCode)
1918
    {
1919
        $courseCode = Database::escape_string($courseCode);
1920
        $sql = "SELECT count(skill_id) as count
1921
                FROM {$this->table_gradebook} g
1922
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1923
                ON g.id = sg.gradebook_id
1924
                WHERE course_code = '$courseCode'";
1925
1926
        $result = Database::query($sql);
1927
        if (Database::num_rows($result)) {
1928
            $result = Database::fetch_row($result);
1929
1930
            return $result[0];
1931
        }
1932
1933
        return 0;
1934
    }
1935
1936
    /**
1937
     * @param int $skillId
1938
     *
1939
     * @return array
1940
     */
1941
    public function getCoursesBySkill($skillId)
1942
    {
1943
        $skillId = (int) $skillId;
1944
        $sql = "SELECT c.title, c.code
1945
                FROM {$this->table_gradebook} g
1946
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1947
                ON g.id = sg.gradebook_id
1948
                INNER JOIN {$this->table_course} c
1949
                ON c.code = g.course_code
1950
                WHERE sg.skill_id = $skillId
1951
                AND (g.session_id IS NULL OR g.session_id = 0)";
1952
        $result = Database::query($sql);
1953
1954
        return Database::store_result($result, 'ASSOC');
1955
    }
1956
1957
    /**
1958
     * Check if the user has the skill.
1959
     *
1960
     * @param int $userId    The user id
1961
     * @param int $skillId   The skill id
1962
     * @param int $courseId  Optional. The course id
1963
     * @param int $sessionId Optional. The session id
1964
     *
1965
     * @return bool Whether the user has the skill return true. Otherwise return false
1966
     */
1967
    public function userHasSkill($userId, $skillId, $courseId = 0, $sessionId = 0)
1968
    {
1969
        $courseId = (int) $courseId;
1970
        $sessionId = (int) $sessionId;
1971
1972
        $whereConditions = [
1973
            'user_id = ? ' => (int) $userId,
1974
            'AND skill_id = ? ' => (int) $skillId,
1975
        ];
1976
1977
        if ($courseId > 0) {
1978
            $whereConditions['AND course_id = ? '] = $courseId;
1979
            $whereConditions['AND session_id = ? '] = $sessionId ? $sessionId : null;
1980
        }
1981
1982
        $result = Database::select(
1983
            'COUNT(1) AS qty',
1984
            $this->table_skill_rel_user,
1985
            [
1986
                'where' => $whereConditions,
1987
            ],
1988
            'first'
1989
        );
1990
1991
        if ($result != false) {
1992
            if ($result['qty'] > 0) {
1993
                return true;
1994
            }
1995
        }
1996
1997
        return false;
1998
    }
1999
2000
    /**
2001
     * Check if a skill is searched.
2002
     *
2003
     * @param int $id The skill id
2004
     *
2005
     * @return bool Whether el skill is searched return true. Otherwise return false
2006
     */
2007
    public static function isSearched($id)
2008
    {
2009
        $id = (int) $id;
2010
2011
        if (empty($id)) {
2012
            return false;
2013
        }
2014
2015
        $skillRelProfileTable = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
2016
2017
        $result = Database::select(
2018
            'COUNT( DISTINCT `skill_id`) AS qty',
2019
            $skillRelProfileTable,
2020
            [
2021
                'where' => [
2022
                    'skill_id = ?' => $id,
2023
                ],
2024
            ],
2025
            'first'
2026
        );
2027
2028
        if ($result === false) {
2029
            return false;
2030
        }
2031
2032
        if ($result['qty'] > 0) {
2033
            return true;
2034
        }
2035
2036
        return false;
2037
    }
2038
2039
    /**
2040
     * Get the achieved skills by course.
2041
     *
2042
     * @param int $courseId The course id
2043
     *
2044
     * @return array The skills list
2045
     */
2046
    public function listAchievedByCourse($courseId)
2047
    {
2048
        $courseId = (int) $courseId;
2049
2050
        if ($courseId == 0) {
2051
            return [];
2052
        }
2053
2054
        $list = [];
2055
2056
        $sql = "SELECT
2057
                    course.id c_id,
2058
                    course.title c_name,
2059
                    course.directory c_directory,
2060
                    user.user_id,
2061
                    user.lastname,
2062
                    user.firstname,
2063
                    user.username,
2064
                    skill.id skill_id,
2065
                    skill.name skill_name,
2066
                    sru.acquired_skill_at
2067
                FROM {$this->table_skill_rel_user} AS sru
2068
                INNER JOIN {$this->table_course}
2069
                ON sru.course_id = course.id
2070
                INNER JOIN {$this->table_user}
2071
                ON sru.user_id = user.user_id
2072
                INNER JOIN {$this->table}
2073
                ON sru.skill_id = skill.id
2074
                WHERE course.id = $courseId";
2075
2076
        $result = Database::query($sql);
2077
2078
        while ($row = Database::fetch_assoc($result)) {
2079
            $row['skill_name'] = self::translateName($row['skill_name']);
2080
            $list[] = $row;
2081
        }
2082
2083
        return $list;
2084
    }
2085
2086
    /**
2087
     * Get the users list who achieved a skill.
2088
     *
2089
     * @param int $skillId The skill id
2090
     *
2091
     * @return array The users list
2092
     */
2093
    public function listUsersWhoAchieved($skillId)
2094
    {
2095
        $skillId = (int) $skillId;
2096
2097
        if ($skillId == 0) {
2098
            return [];
2099
        }
2100
2101
        $list = [];
2102
        $sql = "SELECT
2103
                    course.id c_id,
2104
                    course.title c_name,
2105
                    course.directory c_directory,
2106
                    user.user_id,
2107
                    user.lastname,
2108
                    user.firstname,
2109
                    user.username,
2110
                    skill.id skill_id,
2111
                    skill.name skill_name,
2112
                    sru.acquired_skill_at
2113
                FROM {$this->table_skill_rel_user} AS sru
2114
                INNER JOIN {$this->table_course}
2115
                ON sru.course_id = course.id
2116
                INNER JOIN {$this->table_user}
2117
                ON sru.user_id = user.user_id
2118
                INNER JOIN {$this->table}
2119
                ON sru.skill_id = skill.id
2120
                WHERE skill.id = $skillId ";
2121
2122
        $result = Database::query($sql);
2123
        while ($row = Database::fetch_assoc($result)) {
2124
            $row['skill_name'] = self::translateName($row['skill_name']);
2125
            $list[] = $row;
2126
        }
2127
2128
        return $list;
2129
    }
2130
2131
    /**
2132
     * Get the session list where the user can achieve a skill.
2133
     *
2134
     * @param int $skillId The skill id
2135
     *
2136
     * @return array
2137
     */
2138
    public function getSessionsBySkill($skillId)
2139
    {
2140
        $skillId = (int) $skillId;
2141
2142
        $sql = "SELECT s.id, s.name
2143
                FROM {$this->table_gradebook} g
2144
                INNER JOIN {$this->table_skill_rel_gradebook} sg
2145
                ON g.id = sg.gradebook_id
2146
                INNER JOIN {$this->sessionTable} s
2147
                ON g.session_id = s.id
2148
                WHERE sg.skill_id = $skillId
2149
                AND g.session_id > 0";
2150
2151
        $result = Database::query($sql);
2152
2153
        return Database::store_result($result, 'ASSOC');
2154
    }
2155
2156
    /**
2157
     * Check if the $fromUser can comment the $toUser skill issue.
2158
     *
2159
     * @param User $fromUser
2160
     * @param User $toUser
2161
     *
2162
     * @return bool
2163
     */
2164
    public static function userCanAddFeedbackToUser($fromUser, $toUser)
2165
    {
2166
        if (api_is_platform_admin()) {
2167
            return true;
2168
        }
2169
2170
        $userRepo = UserManager::getRepository();
2171
        $fromUserStatus = $fromUser->getStatus();
2172
2173
        switch ($fromUserStatus) {
2174
            case SESSIONADMIN:
2175
                if (api_get_setting('allow_session_admins_to_manage_all_sessions') === 'true') {
2176
                    if ($toUser->getCreatorId() === $fromUser->getId()) {
2177
                        return true;
2178
                    }
2179
                }
2180
2181
                $sessionAdmins = $userRepo->getSessionAdmins($toUser);
2182
2183
                foreach ($sessionAdmins as $sessionAdmin) {
2184
                    if ($sessionAdmin->getId() !== $fromUser->getId()) {
2185
                        continue;
2186
                    }
2187
2188
                    return true;
2189
                }
2190
                break;
2191
            case STUDENT_BOSS:
2192
                $studentBosses = $userRepo->getStudentBosses($toUser);
2193
                foreach ($studentBosses as $studentBoss) {
2194
                    if ($studentBoss->getId() !== $fromUser->getId()) {
2195
                        continue;
2196
                    }
2197
2198
                    return true;
2199
                }
2200
                break;
2201
            case DRH:
2202
                return UserManager::is_user_followed_by_drh(
2203
                    $toUser->getId(),
2204
                    $fromUser->getId()
2205
                );
2206
        }
2207
2208
        return false;
2209
    }
2210
2211
    /**
2212
     * If $studentId is set then check if current user has the right to see
2213
     * the page.
2214
     *
2215
     * @param int  $studentId check if current user has access to see $studentId
2216
     * @param bool $blockPage raise a api_not_allowed()
2217
     *
2218
     * @return bool
2219
     */
2220
    public static function isAllowed($studentId = 0, $blockPage = true)
2221
    {
2222
        $allowHR = api_get_setting('allow_hr_skills_management') === 'true';
2223
2224
        if (self::isToolAvailable()) {
2225
            if (api_is_platform_admin(false, $allowHR)) {
2226
                return true;
2227
            }
2228
2229
            if (!empty($studentId)) {
2230
                $currentUserId = api_get_user_id();
2231
                if ((int) $currentUserId === (int) $studentId) {
2232
                    return true;
2233
                }
2234
2235
                $haveAccess = self::hasAccessToUserSkill(
2236
                    $currentUserId,
2237
                    $studentId
2238
                );
2239
2240
                if ($haveAccess) {
2241
                    return true;
2242
                }
2243
            }
2244
        }
2245
2246
        if ($blockPage) {
2247
            api_not_allowed(true);
2248
        }
2249
2250
        return false;
2251
    }
2252
2253
    /**
2254
     * @return bool
2255
     */
2256
    public static function isToolAvailable()
2257
    {
2258
        $allowTool = api_get_setting('allow_skills_tool');
2259
2260
        if ($allowTool === 'true') {
2261
            return true;
2262
        }
2263
2264
        return false;
2265
    }
2266
2267
    /**
2268
     * @param int $currentUserId
2269
     * @param int $studentId
2270
     *
2271
     * @return bool
2272
     */
2273
    public static function hasAccessToUserSkill($currentUserId, $studentId)
2274
    {
2275
        if (self::isToolAvailable()) {
2276
            if (api_is_platform_admin()) {
2277
                return true;
2278
            }
2279
2280
            $currentUserId = (int) $currentUserId;
2281
            $studentId = (int) $studentId;
2282
2283
            if ($currentUserId === $studentId) {
2284
                return true;
2285
            }
2286
2287
            if (api_is_student_boss()) {
2288
                $isBoss = UserManager::userIsBossOfStudent($currentUserId, $studentId);
2289
                if ($isBoss) {
2290
                    return true;
2291
                }
2292
            }
2293
2294
            $allow = api_get_configuration_value('allow_private_skills');
2295
            if ($allow === true) {
2296
                if (api_is_teacher()) {
2297
                    return UserManager::isTeacherOfStudent(
2298
                        $currentUserId,
2299
                        $studentId
2300
                    );
2301
                }
2302
2303
                if (api_is_drh()) {
2304
                    return UserManager::is_user_followed_by_drh(
2305
                        $studentId,
2306
                        $currentUserId
2307
                    );
2308
                }
2309
            }
2310
        }
2311
2312
        return false;
2313
    }
2314
2315
    /**
2316
     * Get skills.
2317
     *
2318
     * @param int $userId
2319
     * @param int level
2320
     *
2321
     * @return array
2322
     */
2323
    public function getStudentSkills($userId, $level = 0)
2324
    {
2325
        $userId = (int) $userId;
2326
2327
        $sql = "SELECT s.id, s.name, sru.acquired_skill_at
2328
                FROM {$this->table} s
2329
                INNER JOIN {$this->table_skill_rel_user} sru
2330
                ON s.id = sru.skill_id
2331
                WHERE sru.user_id = $userId";
2332
2333
        $result = Database::query($sql);
2334
2335
        $skills = [];
2336
        foreach ($result as $item) {
2337
            if (empty($level)) {
2338
                $skills[] = [
2339
                    'name' => self::translateName($item['name']),
2340
                    'acquired_skill_at' => $item['acquired_skill_at'],
2341
                ];
2342
            } else {
2343
                $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

2343
                /** @scrutinizer ignore-call */ 
2344
                $parents = self::get_parents($item['id']);
Loading history...
2344
                // +2 because it takes into account the root
2345
                if (count($parents) == $level + 1) {
2346
                    $skills[] = [
2347
                        'name' => self::translateName($item['name']),
2348
                        'acquired_skill_at' => $item['acquired_skill_at'],
2349
                    ];
2350
                }
2351
            }
2352
        }
2353
2354
        return $skills;
2355
    }
2356
2357
    /**
2358
     * @param string $name
2359
     *
2360
     * @return string
2361
     */
2362
    public static function translateName($name)
2363
    {
2364
        $variable = ChamiloApi::getLanguageVar($name, 'Skill');
2365
2366
        return isset($GLOBALS[$variable]) ? $GLOBALS[$variable] : $name;
2367
    }
2368
2369
    /**
2370
     * @param string $code
2371
     *
2372
     * @return mixed|string
2373
     */
2374
    public static function translateCode($code)
2375
    {
2376
        if (empty($code)) {
2377
            return '';
2378
        }
2379
2380
        $variable = ChamiloApi::getLanguageVar($code, 'SkillCode');
2381
2382
        return isset($GLOBALS[$variable]) ? $GLOBALS[$variable] : $code;
2383
    }
2384
2385
    /**
2386
     * @param FormValidator $form
2387
     * @param array         $skillInfo
2388
     *
2389
     * @return array
2390
     */
2391
    public function setForm(FormValidator &$form, $skillInfo = [])
2392
    {
2393
        $allSkills = $this->get_all();
2394
        $objGradebook = new Gradebook();
2395
2396
        $isAlreadyRootSkill = false;
2397
        foreach ($allSkills as $checkedSkill) {
2398
            if (intval($checkedSkill['parent_id']) > 0) {
2399
                $isAlreadyRootSkill = true;
2400
                break;
2401
            }
2402
        }
2403
2404
        $skillList = $isAlreadyRootSkill ? [] : [0 => get_lang('None')];
2405
2406
        foreach ($allSkills as $skill) {
2407
            if (isset($skillInfo['id']) && $skill['id'] == $skillInfo['id']) {
2408
                continue;
2409
            }
2410
2411
            $skillList[$skill['id']] = $skill['name'];
2412
        }
2413
2414
        $allGradeBooks = $objGradebook->find('all');
2415
2416
        // This procedure is for check if there is already a Skill with no Parent (Root by default)
2417
        $gradeBookList = [];
2418
        foreach ($allGradeBooks as $gradebook) {
2419
            $gradeBookList[$gradebook['id']] = $gradebook['name'];
2420
        }
2421
2422
        $translateUrl = api_get_path(WEB_CODE_PATH).'admin/skill_translate.php?';
2423
        $translateNameButton = '';
2424
        $translateCodeButton = '';
2425
        $skillId = null;
2426
        if (!empty($skillInfo)) {
2427
            $skillId = $skillInfo['id'];
2428
            $translateNameUrl = $translateUrl.http_build_query(['skill' => $skillId, 'action' => 'name']);
2429
            $translateCodeUrl = $translateUrl.http_build_query(['skill' => $skillId, 'action' => 'code']);
2430
            $translateNameButton = Display::toolbarButton(
2431
                get_lang('TranslateThisTerm'),
2432
                $translateNameUrl,
2433
                'language',
2434
                'link'
2435
            );
2436
            $translateCodeButton = Display::toolbarButton(
2437
                get_lang('TranslateThisTerm'),
2438
                $translateCodeUrl,
2439
                'language',
2440
                'link'
2441
            );
2442
        }
2443
2444
        $form->addText('name', [get_lang('Name'), $translateNameButton], true, ['id' => 'name']);
2445
        $form->addText('short_code', [get_lang('ShortCode'), $translateCodeButton], false, ['id' => 'short_code']);
2446
2447
        // Cannot change parent of root
2448
        if ($skillId != 1) {
2449
            $form->addSelect('parent_id', get_lang('Parent'), $skillList, ['id' => 'parent_id']);
2450
        }
2451
2452
        $form->addSelect(
2453
            'gradebook_id',
2454
            [get_lang('Gradebook'), get_lang('WithCertificate')],
2455
            $gradeBookList,
2456
            ['id' => 'gradebook_id', 'multiple' => 'multiple', 'size' => 10]
2457
        );
2458
        $form->addTextarea('description', get_lang('Description'), ['id' => 'description', 'rows' => 7]);
2459
        $form->addTextarea('criteria', get_lang('CriteriaToEarnTheBadge'), ['id' => 'criteria', 'rows' => 7]);
2460
2461
        // EXTRA FIELDS
2462
        $extraField = new ExtraField('skill');
2463
        $returnParams = $extraField->addElements($form, $skillId);
2464
2465
        if (empty($skillInfo)) {
2466
            $form->addButtonCreate(get_lang('Add'));
2467
        } else {
2468
            $form->addButtonUpdate(get_lang('Update'));
2469
            $form->addHidden('id', $skillInfo['id']);
2470
        }
2471
2472
        return $returnParams;
2473
    }
2474
2475
    /**
2476
     * @return string
2477
     */
2478
    public function getToolBar()
2479
    {
2480
        $toolbar = Display::url(
2481
            Display::return_icon(
2482
                'back.png',
2483
                get_lang('ManageSkills'),
2484
                null,
2485
                ICON_SIZE_MEDIUM
2486
            ),
2487
            api_get_path(WEB_CODE_PATH).'admin/skill_list.php'
2488
        );
2489
        $actions = '<div class="actions">'.$toolbar.'</div>';
2490
2491
        return $actions;
2492
    }
2493
2494
    /**
2495
     * @param \Chamilo\SkillBundle\Entity\SkillRelItem        $skillRelItem
2496
     * @param \Chamilo\SkillBundle\Entity\SkillRelItemRelUser $skillRelItemRelUser
2497
     * @param bool                                            $addHeader
2498
     *
2499
     * @return string
2500
     */
2501
    public static function getUserSkillStatusLabel($skillRelItem, $skillRelItemRelUser, $addHeader = true)
2502
    {
2503
        if (empty($skillRelItem)) {
2504
            return '';
2505
        }
2506
        $type = 'success';
2507
        if (empty($skillRelItemRelUser)) {
2508
            $type = 'danger';
2509
        }
2510
        $label = '';
2511
        $skill = $skillRelItem->getSkill();
2512
        if ($addHeader) {
2513
            $label .= '<span id="'.$skill->getId().'" class="user_skill" style="cursor:pointer">';
2514
        }
2515
        $label .= Display::label($skill->getName(), $type);
2516
        if ($addHeader) {
2517
            $label .= '</span>&nbsp;';
2518
        }
2519
2520
        return $label;
2521
    }
2522
2523
    /**
2524
     * Assign a user with a SkilRelItem object.
2525
     *
2526
     * @param FormValidator $form
2527
     * @param int           $typeId see ITEM_TYPE_* constants
2528
     * @param int           $itemId
2529
     * @param int           $userId
2530
     */
2531
    public static function addSkillsToUserForm(FormValidator $form, $typeId, $itemId, $userId, $resultId = 0, $addHeader = false)
2532
    {
2533
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2534
        if ($allowSkillInTools && !empty($typeId) && !empty($itemId) && !empty($userId)) {
2535
            $em = Database::getManager();
2536
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2537
                ['itemId' => $itemId, 'itemType' => $typeId]
2538
            );
2539
2540
            $skillRelUser = new SkillRelUser();
2541
            $skillUserList = $skillRelUser->getUserSkills($userId);
2542
            if (!empty($skillUserList)) {
2543
                $skillUserList = array_column($skillUserList, 'skill_id');
2544
            }
2545
2546
            $skills = '';
2547
            /** @var SkillRelItem $skillRelItem */
2548
            foreach ($items as $skillRelItem) {
2549
                $criteria = [
2550
                    'user' => $userId,
2551
                    'skillRelItem' => $skillRelItem,
2552
                ];
2553
                $skillRelItemRelUser = $em->getRepository('ChamiloSkillBundle:SkillRelItemRelUser')->findOneBy($criteria);
2554
                $skills .= self::getUserSkillStatusLabel($skillRelItem, $skillRelItemRelUser);
2555
            }
2556
2557
            if (!empty($skills)) {
2558
                $url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=update_skill_rel_user&'.api_get_cidreq();
2559
                $params = [
2560
                    'item_id' => $itemId,
2561
                    'type_id' => $typeId,
2562
                    'user_id' => $userId,
2563
                    'course_id' => api_get_course_int_id(),
2564
                    'session_id' => api_get_session_id(),
2565
                    'result_id' => $resultId,
2566
                ];
2567
                $params = json_encode($params);
2568
                if ($addHeader) {
2569
                    $form->addHtml(Display::page_subheader2(get_lang('Skills')));
2570
                }
2571
                $html = '
2572
                <script>
2573
                    $(function() {
2574
                        $(".user_skill").on("click", function() {
2575
                            var skillId = this.id;
2576
                            var params = '.$params.';
2577
                            $.ajax({
2578
                                type: "GET",
2579
                                async: false,
2580
                                data: params,
2581
                                url: "'.$url.'&skill_id="+skillId,
2582
                                success: function(result) {
2583
                                    $("#" +skillId+ ".user_skill").html(result);
2584
                                }
2585
                            });                            
2586
                        });                        
2587
                    });
2588
                </script>
2589
                ';
2590
                $form->addHtml($html);
2591
                $form->addLabel(get_lang('Skills'), $skills);
2592
                if ($addHeader) {
2593
                    $form->addHtml('<br />');
2594
                }
2595
            }
2596
        }
2597
    }
2598
2599
    /**
2600
     * Add skills select ajax for an item (exercise, lp).
2601
     *
2602
     * @param FormValidator $form
2603
     * @param int           $typeId see ITEM_TYPE_* constants
2604
     * @param int           $itemId
2605
     *
2606
     * @throws Exception
2607
     *
2608
     * @return array
2609
     */
2610
    public static function addSkillsToForm(FormValidator $form, $typeId, $itemId = 0)
2611
    {
2612
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2613
        if (!$allowSkillInTools) {
2614
            return [];
2615
        }
2616
2617
        $skillList = [];
2618
        if (!empty($itemId)) {
2619
            $em = Database::getManager();
2620
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2621
                ['itemId' => $itemId, 'itemType' => $typeId]
2622
            );
2623
            /** @var SkillRelItem $skillRelItem */
2624
            foreach ($items as $skillRelItem) {
2625
                $skillList[$skillRelItem->getSkill()->getId()] = $skillRelItem->getSkill()->getName();
2626
            }
2627
        }
2628
2629
        $courseId = api_get_course_int_id();
2630
        $sessionId = api_get_session_id();
2631
2632
        $url = api_get_path(WEB_AJAX_PATH).
2633
            'skill.ajax.php?a=search_skills_in_course&course_id='.$courseId.'&session_id='.$sessionId;
2634
        $form->addSelectAjax(
2635
            'skills',
2636
            get_lang('Skills'),
2637
            $skillList,
2638
            [
2639
                'url' => $url,
2640
                'multiple' => 'multiple',
2641
            ]
2642
        );
2643
2644
        return $skillList;
2645
    }
2646
2647
    /**
2648
     * @param int $courseId
2649
     * @param int $sessionId
2650
     *
2651
     * @return array
2652
     */
2653
    public static function getSkillRelItemsPerCourse($courseId, $sessionId = null)
2654
    {
2655
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2656
        $skills = [];
2657
2658
        if (empty($sessionId)) {
2659
            $sessionId = null;
2660
        }
2661
2662
        if ($allowSkillInTools) {
2663
            $em = Database::getManager();
2664
            $skills = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2665
                ['courseId' => $courseId, 'sessionId' => $sessionId]
2666
            );
2667
        }
2668
2669
        return $skills;
2670
    }
2671
2672
    /**
2673
     * @param int $itemId
2674
     * @param int $itemType
2675
     *
2676
     * @return array
2677
     */
2678
    public static function getItemInfo($itemId, $itemType)
2679
    {
2680
        $itemInfo = [];
2681
        $itemId = (int) $itemId;
2682
        $itemType = (int) $itemType;
2683
        $em = Database::getManager();
2684
2685
        switch ($itemType) {
2686
            case ITEM_TYPE_EXERCISE:
2687
                /** @var \Chamilo\CourseBundle\Entity\CQuiz $item */
2688
                $item = $em->getRepository('ChamiloCourseBundle:CQuiz')->find($itemId);
2689
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CQuiz, thus it always evaluated to true.
Loading history...
2690
                    $itemInfo['name'] = $item->getTitle();
2691
                }
2692
                break;
2693
            case ITEM_TYPE_HOTPOTATOES:
2694
                break;
2695
            case ITEM_TYPE_LINK:
2696
                /** @var \Chamilo\CourseBundle\Entity\CLink $item */
2697
                $item = $em->getRepository('ChamiloCourseBundle:CLink')->find($itemId);
2698
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CLink, thus it always evaluated to true.
Loading history...
2699
                    $itemInfo['name'] = $item->getTitle();
2700
                }
2701
                break;
2702
            case ITEM_TYPE_LEARNPATH:
2703
                /** @var \Chamilo\CourseBundle\Entity\CLp $item */
2704
                $item = $em->getRepository('ChamiloCourseBundle:CLp')->find($itemId);
2705
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CLp, thus it always evaluated to true.
Loading history...
2706
                    $itemInfo['name'] = $item->getName();
2707
                }
2708
                break;
2709
            case ITEM_TYPE_GRADEBOOK:
2710
                break;
2711
            case ITEM_TYPE_STUDENT_PUBLICATION:
2712
                /** @var \Chamilo\CourseBundle\Entity\CStudentPublication $item */
2713
                $item = $em->getRepository('ChamiloCourseBundle:CStudentPublication')->find($itemId);
2714
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CStudentPublication, thus it always evaluated to true.
Loading history...
2715
                    $itemInfo['name'] = $item->getTitle();
2716
                }
2717
                break;
2718
            //ITEM_TYPE_FORUM', 7);
2719
            case ITEM_TYPE_ATTENDANCE:
2720
                /** @var \Chamilo\CourseBundle\Entity\CAttendance $item */
2721
                $item = $em->getRepository('ChamiloCourseBundle:CAttendance')->find($itemId);
2722
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CAttendance, thus it always evaluated to true.
Loading history...
2723
                    $itemInfo['name'] = $item->getName();
2724
                }
2725
                break;
2726
            case ITEM_TYPE_SURVEY:
2727
                /** @var \Chamilo\CourseBundle\Entity\CSurvey $item */
2728
                $item = $em->getRepository('ChamiloCourseBundle:CSurvey')->find($itemId);
2729
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CSurvey, thus it always evaluated to true.
Loading history...
2730
                    $itemInfo['name'] = strip_tags($item->getTitle());
2731
                }
2732
                break;
2733
            case ITEM_TYPE_FORUM_THREAD:
2734
                /** @var \Chamilo\CourseBundle\Entity\CForumThread $item */
2735
                $item = $em->getRepository('ChamiloCourseBundle:CForumThread')->find($itemId);
2736
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CForumThread, thus it always evaluated to true.
Loading history...
2737
                    $itemInfo['name'] = $item->getThreadTitle();
2738
                }
2739
                break;
2740
        }
2741
2742
        return $itemInfo;
2743
    }
2744
2745
    /**
2746
     * @param int $typeId
2747
     * @param int $itemId
2748
     *
2749
     * @return array
2750
     */
2751
    public static function getSkillRelItems($typeId, $itemId)
2752
    {
2753
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2754
        $skills = [];
2755
        if ($allowSkillInTools) {
2756
            $em = Database::getManager();
2757
            $skills = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2758
                ['itemId' => $itemId, 'itemType' => $typeId]
2759
            );
2760
        }
2761
2762
        return $skills;
2763
    }
2764
2765
    /**
2766
     * @param int $typeId
2767
     * @param int $itemId
2768
     *
2769
     * @return string
2770
     */
2771
    public static function getSkillRelItemsToString($typeId, $itemId)
2772
    {
2773
        $skills = self::getSkillRelItems($typeId, $itemId);
2774
        $skillToString = '';
2775
        if (!empty($skills)) {
2776
            /** @var SkillRelItem $skillRelItem */
2777
            $skillList = [];
2778
            foreach ($skills as $skillRelItem) {
2779
                $skillList[] = Display::label($skillRelItem->getSkill()->getName(), 'success');
2780
            }
2781
            $skillToString = '&nbsp;'.implode(' ', $skillList);
2782
        }
2783
2784
        return $skillToString;
2785
    }
2786
2787
    /**
2788
     * @param int $itemId
2789
     * @param int $typeId
2790
     */
2791
    public static function deleteSkillsFromItem($itemId, $typeId)
2792
    {
2793
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2794
        if ($allowSkillInTools) {
2795
            $itemId = (int) $itemId;
2796
            $typeId = (int) $typeId;
2797
2798
            $em = Database::getManager();
2799
            // Delete old ones
2800
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2801
                ['itemId' => $itemId, 'itemType' => $typeId]
2802
            );
2803
2804
            /** @var SkillRelItem $skillRelItem */
2805
            foreach ($items as $skillRelItem) {
2806
                $em->remove($skillRelItem);
2807
            }
2808
            $em->flush();
2809
        }
2810
    }
2811
2812
    /**
2813
     * Relate skill with an item (exercise, gradebook, lp, etc).
2814
     *
2815
     * @param FormValidator $form
2816
     * @param int           $typeId
2817
     * @param int           $itemId
2818
     *
2819
     * @throws \Doctrine\ORM\OptimisticLockException
2820
     */
2821
    public static function saveSkills($form, $typeId, $itemId)
2822
    {
2823
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2824
        if ($allowSkillInTools) {
2825
            $userId = api_get_user_id();
2826
            $courseId = api_get_course_int_id();
2827
            if (empty($courseId)) {
2828
                $courseId = null;
2829
            }
2830
            $sessionId = api_get_session_id();
2831
            if (empty($sessionId)) {
2832
                $sessionId = null;
2833
            }
2834
2835
            $em = Database::getManager();
2836
            $skills = (array) $form->getSubmitValue('skills');
2837
2838
            // Delete old ones
2839
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2840
                ['itemId' => $itemId, 'itemType' => $typeId]
2841
            );
2842
            if (!empty($items)) {
2843
                /** @var SkillRelItem $skillRelItem */
2844
                foreach ($items as $skillRelItem) {
2845
                    if (!in_array($skillRelItem->getSkill()->getId(), $skills)) {
2846
                        $em->remove($skillRelItem);
2847
                    }
2848
                }
2849
                $em->flush();
2850
            }
2851
2852
            // Add new one
2853
            if (!empty($skills)) {
2854
                foreach ($skills as $skillId) {
2855
                    /** @var SkillEntity $skill */
2856
                    $skill = $em->getRepository('ChamiloCoreBundle:Skill')->find($skillId);
2857
                    if ($skill) {
2858
                        if (!$skill->hasItem($typeId, $itemId)) {
2859
                            $skillRelItem = new SkillRelItem();
2860
                            $skillRelItem
2861
                                ->setItemType($typeId)
2862
                                ->setItemId($itemId)
2863
                                ->setCourseId($courseId)
2864
                                ->setSessionId($sessionId)
2865
                                ->setCreatedBy($userId)
2866
                                ->setUpdatedBy($userId)
2867
                            ;
2868
                            $skill->addItem($skillRelItem);
2869
                            $em->persist($skill);
2870
                            $em->flush();
2871
                        }
2872
                    }
2873
                }
2874
            }
2875
        }
2876
    }
2877
2878
    /**
2879
     * Relate skill with an item (exercise, gradebook, lp, etc).
2880
     *
2881
     * @param FormValidator $form
2882
     *
2883
     * @return bool
2884
     */
2885
    public static function saveSkillsToCourseFromForm(FormValidator $form)
2886
    {
2887
        $skills = (array) $form->getSubmitValue('skills');
2888
        $courseId = (int) $form->getSubmitValue('course_id');
2889
        $sessionId = $form->getSubmitValue('session_id');
2890
2891
        return self::saveSkillsToCourse($skills, $courseId, $sessionId);
2892
    }
2893
2894
    /**
2895
     * @param array $skills
2896
     * @param int   $courseId
2897
     * @param int   $sessionId
2898
     *
2899
     * @throws \Doctrine\ORM\OptimisticLockException
2900
     *
2901
     * @return bool
2902
     */
2903
    public static function saveSkillsToCourse($skills, $courseId, $sessionId)
2904
    {
2905
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2906
        if (!$allowSkillInTools) {
2907
            return false;
2908
        }
2909
2910
        $em = Database::getManager();
2911
        $sessionId = empty($sessionId) ? null : (int) $sessionId;
2912
2913
        $course = api_get_course_entity($courseId);
2914
        if (empty($course)) {
2915
            return false;
2916
        }
2917
        $session = null;
2918
        if (!empty($sessionId)) {
2919
            $session = api_get_session_entity($sessionId);
2920
            $courseExistsInSession = SessionManager::sessionHasCourse($sessionId, $course->getCode());
2921
            if (!$courseExistsInSession) {
2922
                return false;
2923
            }
2924
        }
2925
2926
        // Delete old ones
2927
        $items = $em->getRepository('ChamiloSkillBundle:SkillRelCourse')->findBy(
2928
            ['course' => $courseId, 'session' => $sessionId]
2929
        );
2930
2931
        if (!empty($items)) {
2932
            /** @var SkillRelCourse $item */
2933
            foreach ($items as $item) {
2934
                if (!in_array($item->getSkill()->getId(), $skills)) {
2935
                    $em->remove($item);
2936
                }
2937
            }
2938
            $em->flush();
2939
        }
2940
2941
        // Add new one
2942
        if (!empty($skills)) {
2943
            foreach ($skills as $skillId) {
2944
                $item = new SkillRelCourse();
2945
                $item->setCourse($course);
2946
                $item->setSession($session);
2947
2948
                /** @var SkillEntity $skill */
2949
                $skill = $em->getRepository('ChamiloCoreBundle:Skill')->find($skillId);
2950
                if ($skill) {
2951
                    if (!$skill->hasCourseAndSession($item)) {
2952
                        $skill->addToCourse($item);
2953
                        $em->persist($skill);
2954
                    }
2955
                }
2956
            }
2957
            $em->flush();
2958
        }
2959
2960
        return true;
2961
    }
2962
2963
    /**
2964
     * Get the icon (badge image) URL.
2965
     *
2966
     * @param SkillEntity $skill
2967
     * @param bool        $getSmall Optional. Allow get the small image
2968
     *
2969
     * @return string
2970
     */
2971
    public static function getWebIconPath(SkillEntity $skill, $getSmall = false)
2972
    {
2973
        if ($getSmall) {
2974
            if (empty($skill->getIcon())) {
2975
                return \Display::return_icon('badges-default.png', null, null, ICON_SIZE_BIG, null, true);
2976
            }
2977
2978
            return api_get_path(WEB_UPLOAD_PATH).'badges/'.sha1($skill->getName()).'-small.png';
2979
        }
2980
2981
        if (empty($skill->getIcon())) {
2982
            return \Display::return_icon('badges-default.png', null, null, ICON_SIZE_HUGE, null, true);
2983
        }
2984
2985
        return api_get_path(WEB_UPLOAD_PATH)."badges/{$skill->getIcon()}";
2986
    }
2987
2988
    /**
2989
     * @param User                             $user
2990
     * @param \Chamilo\CoreBundle\Entity\Skill $skill
2991
     * @param int                              $levelId
2992
     * @param string                           $argumentation
2993
     * @param int                              $authorId
2994
     *
2995
     * @throws \Doctrine\ORM\OptimisticLockException
2996
     *
2997
     * @return SkillRelUserEntity
2998
     */
2999
    public function addSkillToUserBadge($user, $skill, $levelId, $argumentation, $authorId)
3000
    {
3001
        $showLevels = api_get_configuration_value('hide_skill_levels') === false;
3002
3003
        $entityManager = Database::getManager();
3004
3005
        $skillUserRepo = $entityManager->getRepository('ChamiloSkillBundle:SkillRelUser');
3006
3007
        $criteria = ['user' => $user, 'skill' => $skill];
3008
        $result = $skillUserRepo->findOneBy($criteria);
3009
3010
        if (!empty($result)) {
3011
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type Chamilo\CoreBundle\Entity\SkillRelUser.
Loading history...
3012
        }
3013
        $skillLevelRepo = $entityManager->getRepository('ChamiloSkillBundle:Level');
3014
3015
        $skillUser = new \Chamilo\CoreBundle\Entity\SkillRelUser();
3016
        $skillUser->setUser($user);
3017
        $skillUser->setSkill($skill);
3018
3019
        if ($showLevels && !empty($levelId)) {
3020
            $level = $skillLevelRepo->find($levelId);
3021
            $skillUser->setAcquiredLevel($level);
3022
        }
3023
3024
        $skillUser->setArgumentation($argumentation);
3025
        $skillUser->setArgumentationAuthorId($authorId);
3026
        $skillUser->setAcquiredSkillAt(new DateTime());
3027
        $skillUser->setAssignedBy(0);
3028
3029
        $entityManager->persist($skillUser);
3030
        $entityManager->flush();
3031
3032
        return $skillUser;
3033
    }
3034
}
3035