Passed
Push — 1.11.x ( 89e3fa...c176cd )
by Julito
09:10
created

Skill::saveSkills()   C

Complexity

Conditions 13
Paths 41

Size

Total Lines 74
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 48
c 1
b 0
f 0
nc 41
nop 3
dl 0
loc 74
rs 6.6166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
class SkillProfile extends Model
19
{
20
    public $columns = ['id', 'name', 'description'];
21
22
    /**
23
     * Constructor.
24
     */
25
    public function __construct()
26
    {
27
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_PROFILE);
28
        $this->table_rel_profile = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
29
    }
30
31
    /**
32
     * @return array
33
     */
34
    public function getProfiles()
35
    {
36
        $sql = "SELECT * FROM $this->table p
37
                INNER JOIN $this->table_rel_profile sp
38
                ON (p.id = sp.profile_id) ";
39
        $result = Database::query($sql);
40
        $profiles = Database::store_result($result, 'ASSOC');
41
42
        return $profiles;
43
    }
44
45
    /**
46
     * This function is for editing profile info from profile_id.
47
     *
48
     * @param int    $profileId
49
     * @param string $name
50
     * @param string $description
51
     *
52
     * @return bool
53
     */
54
    public function updateProfileInfo($profileId, $name, $description)
55
    {
56
        $profileId = (int) $profileId;
57
58
        if (empty($profileId)) {
59
            return false;
60
        }
61
62
        $name = Database::escape_string($name);
63
        $description = Database::escape_string($description);
64
65
        $sql = "UPDATE $this->table SET
66
                    name = '$name',
67
                    description = '$description'
68
                WHERE id = $profileId ";
69
        Database::query($sql);
70
71
        return true;
72
    }
73
74
    /**
75
     * Call the save method of the parent class and the SkillRelProfile object.
76
     *
77
     * @param array $params
78
     * @param bool  $show_query Whether to show the query in parent save() method
79
     *
80
     * @return mixed Profile ID or false if incomplete params
81
     */
82
    public function save($params, $show_query = false)
83
    {
84
        if (!empty($params)) {
85
            $profile_id = parent::save($params, $show_query);
86
            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...
87
                $skill_rel_profile = new SkillRelProfile();
88
                if (isset($params['skills'])) {
89
                    foreach ($params['skills'] as $skill_id) {
90
                        $attributes = [
91
                            'skill_id' => $skill_id,
92
                            'profile_id' => $profile_id,
93
                        ];
94
                        $skill_rel_profile->save($attributes);
95
                    }
96
                }
97
98
                return $profile_id;
99
            }
100
        }
101
102
        return false;
103
    }
104
105
    /**
106
     * Delete a skill profile.
107
     *
108
     * @param int $id The skill profile id
109
     *
110
     * @return bool Whether delete a skill profile
111
     */
112
    public function delete($id)
113
    {
114
        Database::delete(
115
            $this->table_rel_profile,
116
            [
117
                'profile_id' => $id,
118
            ]
119
        );
120
121
        return parent::delete($id);
122
    }
123
}
124
125
/**
126
 * Class SkillRelProfile.
127
 */
128
class SkillRelProfile extends Model
129
{
130
    public $columns = ['id', 'skill_id', 'profile_id'];
131
132
    /**
133
     * Constructor.
134
     */
135
    public function __construct()
136
    {
137
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
138
        $this->tableProfile = Database::get_main_table(TABLE_MAIN_SKILL_PROFILE);
139
    }
140
141
    /**
142
     * @param int $profileId
143
     *
144
     * @return array
145
     */
146
    public function getSkillsByProfile($profileId)
147
    {
148
        $profileId = (int) $profileId;
149
        $skills = $this->get_all(['where' => ['profile_id = ? ' => $profileId]]);
150
        $return = [];
151
        if (!empty($skills)) {
152
            foreach ($skills as $skill_data) {
153
                $return[] = $skill_data['skill_id'];
154
            }
155
        }
156
157
        return $return;
158
    }
159
160
    /**
161
     * This function is for getting profile info from profile_id.
162
     *
163
     * @param int $profileId
164
     *
165
     * @return array
166
     */
167
    public function getProfileInfo($profileId)
168
    {
169
        $profileId = (int) $profileId;
170
        $sql = "SELECT * FROM $this->table p
171
                INNER JOIN $this->tableProfile pr
172
                ON (pr.id = p.profile_id)
173
                WHERE p.profile_id = ".$profileId;
174
        $result = Database::query($sql);
175
        $profileData = Database::fetch_array($result, 'ASSOC');
176
177
        return $profileData;
178
    }
179
}
180
181
/**
182
 * Class SkillRelSkill.
183
 */
184
class SkillRelSkill extends Model
185
{
186
    public $columns = ['skill_id', 'parent_id', 'relation_type', 'level'];
187
188
    /**
189
     * Constructor.
190
     */
191
    public function __construct()
192
    {
193
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
194
        $this->tableSkill = Database::get_main_table(TABLE_MAIN_SKILL);
195
    }
196
197
    /**
198
     * Gets an element.
199
     *
200
     * @param int $id
201
     *
202
     * @return array
203
     */
204
    public function getSkillInfo($id)
205
    {
206
        $id = (int) $id;
207
208
        if (empty($id)) {
209
            return [];
210
        }
211
212
        $result = Database::select(
213
            '*',
214
            $this->table,
215
            ['where' => ['skill_id = ?' => $id]],
216
            'first'
217
        );
218
219
        return $result;
220
    }
221
222
    /**
223
     * @param int  $skillId
224
     * @param bool $add_child_info
225
     *
226
     * @return array
227
     */
228
    public function getSkillParents($skillId, $add_child_info = true)
229
    {
230
        $skillId = (int) $skillId;
231
        $sql = 'SELECT child.* FROM '.$this->table.' child
232
                LEFT JOIN '.$this->table.' parent
233
                ON child.parent_id = parent.skill_id
234
                WHERE child.skill_id = '.$skillId.' ';
235
        $result = Database::query($sql);
236
        $skill = Database::store_result($result, 'ASSOC');
237
        $skill = isset($skill[0]) ? $skill[0] : null;
238
239
        $parents = [];
240
        if (!empty($skill)) {
241
            if ($skill['parent_id'] != null) {
242
                $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

242
                /** @scrutinizer ignore-call */ 
243
                $parents = self::getSkillParents($skill['parent_id']);
Loading history...
243
            }
244
            if ($add_child_info) {
245
                $parents[] = $skill;
246
            }
247
        }
248
249
        return $parents;
250
    }
251
252
    /**
253
     * @param int $skillId
254
     *
255
     * @return array
256
     */
257
    public function getDirectParents($skillId)
258
    {
259
        $skillId = (int) $skillId;
260
        $sql = 'SELECT parent_id as skill_id
261
                FROM '.$this->table.'
262
                WHERE skill_id = '.$skillId;
263
        $result = Database::query($sql);
264
        $skill = Database::store_result($result, 'ASSOC');
265
        $skill = isset($skill[0]) ? $skill[0] : null;
266
        $parents = [];
267
        if (!empty($skill)) {
268
            $parents[] = $skill;
269
        }
270
271
        return $parents;
272
    }
273
274
    /**
275
     * @param int  $skill_id
276
     * @param bool $load_user_data
277
     * @param bool $user_id
278
     *
279
     * @return array
280
     */
281
    public function getChildren(
282
        $skill_id,
283
        $load_user_data = false,
284
        $user_id = false,
285
        $order = ''
286
    ) {
287
        $skill_id = (int) $skill_id;
288
        $sql = 'SELECT parent.* FROM '.$this->tableSkill.' skill
289
                INNER JOIN '.$this->table.' parent
290
                ON parent.id = skill.id
291
                WHERE parent_id = '.$skill_id.'
292
                ORDER BY skill.name ASC';
293
        $result = Database::query($sql);
294
        $skills = Database::store_result($result, 'ASSOC');
295
296
        $skill_obj = new Skill();
297
        $skill_rel_user = new SkillRelUser();
298
299
        if ($load_user_data) {
300
            $passed_skills = $skill_rel_user->getUserSkills($user_id);
301
            $done_skills = [];
302
            foreach ($passed_skills as $done_skill) {
303
                $done_skills[] = $done_skill['skill_id'];
304
            }
305
        }
306
307
        if (!empty($skills)) {
308
            foreach ($skills as &$skill) {
309
                $skill['data'] = $skill_obj->get($skill['skill_id']);
310
                if (isset($skill['data']) && !empty($skill['data'])) {
311
                    if (!empty($done_skills)) {
312
                        $skill['data']['passed'] = 0;
313
                        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...
314
                            $skill['data']['passed'] = 1;
315
                        }
316
                    }
317
                } else {
318
                    $skill = null;
319
                }
320
            }
321
        }
322
323
        return $skills;
324
    }
325
326
    /**
327
     * @param array $params
328
     *
329
     * @return bool
330
     */
331
    public function updateBySkill($params)
332
    {
333
        $result = Database::update(
334
            $this->table,
335
            $params,
336
            ['skill_id = ? ' => $params['skill_id']]
337
        );
338
        if ($result) {
339
            return true;
340
        }
341
342
        return false;
343
    }
344
345
    /**
346
     * @param int $skill_id
347
     * @param int $parent_id
348
     *
349
     * @return bool
350
     */
351
    public function relationExists($skill_id, $parent_id)
352
    {
353
        $result = $this->find(
354
            'all',
355
            [
356
                'where' => [
357
                    'skill_id = ? AND parent_id = ?' => [
358
                        $skill_id,
359
                        $parent_id,
360
                    ],
361
                ],
362
            ]
363
        );
364
365
        if (!empty($result)) {
366
            return true;
367
        }
368
369
        return false;
370
    }
371
}
372
373
/**
374
 * Class SkillRelGradebook.
375
 */
376
class SkillRelGradebook extends Model
377
{
378
    public $columns = ['id', 'gradebook_id', 'skill_id'];
379
380
    /**
381
     * SkillRelGradebook constructor.
382
     */
383
    public function __construct()
384
    {
385
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
386
    }
387
388
    /**
389
     * @param int $gradebookId
390
     * @param int $skillId
391
     *
392
     * @return bool
393
     */
394
    public function existsGradeBookSkill($gradebookId, $skillId)
395
    {
396
        $result = $this->find(
397
            'all',
398
            [
399
                'where' => [
400
                    'gradebook_id = ? AND skill_id = ?' => [
401
                        $gradebookId,
402
                        $skillId,
403
                    ],
404
                ],
405
            ]
406
        );
407
        if (!empty($result)) {
408
            return true;
409
        }
410
411
        return false;
412
    }
413
414
    /**
415
     * Gets an element.
416
     */
417
    public function getSkillInfo($skill_id, $gradebookId)
418
    {
419
        if (empty($skill_id)) {
420
            return [];
421
        }
422
        $result = Database::select(
423
            '*',
424
            $this->table,
425
            [
426
                'where' => [
427
                    'skill_id = ? AND gradebook_id = ? ' => [
428
                        $skill_id,
429
                        $gradebookId,
430
                    ],
431
                ],
432
            ],
433
            'first'
434
        );
435
436
        return $result;
437
    }
438
439
    /**
440
     * @param int   $skill_id
441
     * @param array $gradebook_list
442
     */
443
    public function updateGradeBookListBySkill($skill_id, $gradebook_list)
444
    {
445
        $original_gradebook_list = $this->find(
446
            'all',
447
            ['where' => ['skill_id = ?' => [$skill_id]]]
448
        );
449
        $gradebooks_to_remove = [];
450
        $gradebooks_to_add = [];
451
        $original_gradebook_list_ids = [];
452
453
        if (!empty($original_gradebook_list)) {
454
            foreach ($original_gradebook_list as $gradebook) {
455
                if (!in_array($gradebook['gradebook_id'], $gradebook_list)) {
456
                    $gradebooks_to_remove[] = $gradebook['id'];
457
                }
458
            }
459
            foreach ($original_gradebook_list as $gradebook_item) {
460
                $original_gradebook_list_ids[] = $gradebook_item['gradebook_id'];
461
            }
462
        }
463
464
        if (!empty($gradebook_list)) {
465
            foreach ($gradebook_list as $gradebook_id) {
466
                if (!in_array($gradebook_id, $original_gradebook_list_ids)) {
467
                    $gradebooks_to_add[] = $gradebook_id;
468
                }
469
            }
470
        }
471
472
        if (!empty($gradebooks_to_remove)) {
473
            foreach ($gradebooks_to_remove as $id) {
474
                $this->delete($id);
475
            }
476
        }
477
478
        if (!empty($gradebooks_to_add)) {
479
            foreach ($gradebooks_to_add as $gradebook_id) {
480
                $attributes = [
481
                    'skill_id' => $skill_id,
482
                    'gradebook_id' => $gradebook_id,
483
                ];
484
                $this->save($attributes);
485
            }
486
        }
487
    }
488
489
    /**
490
     * @param array $params
491
     *
492
     * @return bool|void
493
     */
494
    public function updateBySkill($params)
495
    {
496
        $skillInfo = $this->existsGradeBookSkill(
497
            $params['gradebook_id'],
498
            $params['skill_id']
499
        );
500
501
        if ($skillInfo) {
502
            return;
503
        } else {
504
            $result = $this->save($params);
505
        }
506
        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...
507
            return true;
508
        }
509
510
        return false;
511
    }
512
}
513
514
/**
515
 * Class SkillRelUser.
516
 */
517
class SkillRelUser extends Model
518
{
519
    public $columns = [
520
        'id',
521
        'user_id',
522
        'skill_id',
523
        'acquired_skill_at',
524
        'assigned_by',
525
        'course_id',
526
        'session_id',
527
    ];
528
529
    /**
530
     * Constructor.
531
     */
532
    public function __construct()
533
    {
534
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
535
    }
536
537
    /**
538
     * @param array $skill_list
539
     *
540
     * @return array
541
     */
542
    public function getUserBySkills($skill_list)
543
    {
544
        $users = [];
545
        if (!empty($skill_list)) {
546
            $skill_list = array_map('intval', $skill_list);
547
            $skill_list = implode("', '", $skill_list);
548
549
            $sql = "SELECT user_id FROM {$this->table}
550
                    WHERE skill_id IN ('$skill_list') ";
551
552
            $result = Database::query($sql);
553
            $users = Database::store_result($result, 'ASSOC');
554
        }
555
556
        return $users;
557
    }
558
559
    /**
560
     * Get the achieved skills for the user.
561
     *
562
     * @param int $userId
563
     * @param int $courseId  Optional. The course id
564
     * @param int $sessionId Optional. The session id
565
     *
566
     * @return array The skill list. Otherwise return false
567
     */
568
    public function getUserSkills($userId, $courseId = 0, $sessionId = 0)
569
    {
570
        if (empty($userId)) {
571
            return [];
572
        }
573
574
        $courseId = (int) $courseId;
575
        $sessionId = $sessionId ? (int) $sessionId : null;
576
        $whereConditions = [
577
            'user_id = ? ' => (int) $userId,
578
        ];
579
580
        if ($courseId > 0) {
581
            $whereConditions['AND course_id = ? '] = $courseId;
582
            $whereConditions['AND session_id = ?'] = $sessionId;
583
        }
584
585
        $result = Database::select(
586
            'skill_id',
587
            $this->table,
588
            [
589
                'where' => $whereConditions,
590
            ],
591
            'all'
592
        );
593
594
        return $result;
595
    }
596
597
    /**
598
     * Get the relation data between user and skill.
599
     *
600
     * @param int $userId    The user id
601
     * @param int $skillId   The skill id
602
     * @param int $courseId  Optional. The course id
603
     * @param int $sessionId Optional. The session id
604
     *
605
     * @return array The relation data. Otherwise return false
606
     */
607
    public function getByUserAndSkill($userId, $skillId, $courseId = 0, $sessionId = 0)
608
    {
609
        $sql = "SELECT * FROM {$this->table} WHERE user_id = %d AND skill_id = %d ";
610
611
        if ($courseId > 0) {
612
            $sql .= "AND course_id = %d ".api_get_session_condition($sessionId, true);
613
        }
614
615
        $sql = sprintf(
616
            $sql,
617
            $userId,
618
            $skillId,
619
            $courseId
620
        );
621
622
        $result = Database::query($sql);
623
624
        return Database::fetch_assoc($result);
625
    }
626
627
    /**
628
     * Get the URL for the issue.
629
     *
630
     * @return string
631
     */
632
    public static function getIssueUrl(SkillRelUserEntity $skillIssue)
633
    {
634
        return api_get_path(WEB_PATH)."badge/{$skillIssue->getId()}";
635
    }
636
637
    /**
638
     * Get the URL for the All issues page.
639
     *
640
     * @return string
641
     */
642
    public static function getIssueUrlAll(SkillRelUserEntity $skillIssue)
643
    {
644
        return api_get_path(WEB_PATH)."skill/{$skillIssue->getSkill()->getId()}/user/{$skillIssue->getUser()->getId()}";
645
    }
646
647
    /**
648
     * Get the URL for the assertion.
649
     *
650
     * @return string
651
     */
652
    public static function getAssertionUrl(SkillRelUserEntity $skillIssue)
653
    {
654
        $url = api_get_path(WEB_CODE_PATH).'badge/assertion.php?';
655
656
        $url .= http_build_query([
657
            'user' => $skillIssue->getUser()->getId(),
658
            'skill' => $skillIssue->getSkill()->getId(),
659
            'course' => $skillIssue->getCourse() ? $skillIssue->getCourse()->getId() : 0,
660
            'session' => $skillIssue->getSession() ? $skillIssue->getSession()->getId() : 0,
661
        ]);
662
663
        return $url;
664
    }
665
}
666
667
/**
668
 * Class Skill.
669
 */
670
class Skill extends Model
671
{
672
    public $columns = [
673
        'id',
674
        'name',
675
        'description',
676
        'access_url_id',
677
        'short_code',
678
        'icon',
679
        'criteria',
680
    ];
681
    public $required = ['name'];
682
683
    /** Array of colours by depth, for the coffee wheel. Each depth has 4 col */
684
    /*var $colours = array(
685
      0 => array('#f9f0ab', '#ecc099', '#e098b0', '#ebe378'),
686
      1 => array('#d5dda1', '#4a5072', '#8dae43', '#72659d'),
687
      2 => array('#b28647', '#2e6093', '#393e64', '#1e8323'),
688
      3 => array('#9f6652', '#9f6652', '#9f6652', '#9f6652'),
689
      4 => array('#af643c', '#af643c', '#af643c', '#af643c'),
690
      5 => array('#72659d', '#72659d', '#72659d', '#72659d'),
691
      6 => array('#8a6e9e', '#8a6e9e', '#8a6e9e', '#8a6e9e'),
692
      7 => array('#92538c', '#92538c', '#92538c', '#92538c'),
693
      8 => array('#2e6093', '#2e6093', '#2e6093', '#2e6093'),
694
      9 => array('#3a5988', '#3a5988', '#3a5988', '#3a5988'),
695
     10 => array('#393e64', '#393e64', '#393e64', '#393e64'),
696
    );*/
697
    public function __construct()
698
    {
699
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL);
700
        $this->table_user = Database::get_main_table(TABLE_MAIN_USER);
701
        $this->table_skill_rel_gradebook = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
702
        $this->table_skill_rel_user = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
703
        $this->table_course = Database::get_main_table(TABLE_MAIN_COURSE);
704
        $this->table_skill_rel_skill = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
705
        $this->table_gradebook = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
706
        $this->sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
707
    }
708
709
    /**
710
     * Gets an element.
711
     *
712
     * @param int $id
713
     *
714
     * @return array|mixed
715
     */
716
    public function get($id)
717
    {
718
        $result = parent::get($id);
719
        if (empty($result)) {
720
            return [];
721
        }
722
723
        $path = api_get_path(WEB_UPLOAD_PATH).'badges/';
724
        if (!empty($result['icon'])) {
725
            $iconSmall = sprintf(
726
                '%s-small.png',
727
                sha1($result['name'])
728
            );
729
730
            $iconBig = sprintf(
731
                '%s.png',
732
                sha1($result['name'])
733
            );
734
735
            $iconMini = $path.$iconSmall;
736
            $iconSmall = $path.$iconSmall;
737
            $iconBig = $path.$iconBig;
738
        } else {
739
            $iconMini = Display::returnIconPath('badges-default.png', ICON_SIZE_MEDIUM);
740
            $iconSmall = Display::returnIconPath('badges-default.png', ICON_SIZE_BIG);
741
            $iconBig = Display::returnIconPath('badges-default.png', ICON_SIZE_HUGE);
742
        }
743
744
        $result['icon_mini'] = $iconMini;
745
        $result['icon_small'] = $iconSmall;
746
        $result['icon_big'] = $iconBig;
747
748
        $result['img_mini'] = Display::img($iconBig, $result['name'], ['width' => ICON_SIZE_MEDIUM]);
749
        $result['img_big'] = Display::img($iconBig, $result['name']);
750
        $result['img_small'] = Display::img($iconSmall, $result['name']);
751
        $result['name'] = self::translateName($result['name']);
752
        $result['short_code'] = self::translateCode($result['short_code']);
753
754
        return $result;
755
    }
756
757
    /**
758
     * @param array  $skills
759
     * @param string $imageSize     mini|small|big
760
     * @param bool   $addDivWrapper
761
     *
762
     * @return string
763
     */
764
    public function processSkillList($skills, $imageSize = '', $addDivWrapper = true)
765
    {
766
        if (empty($skills)) {
767
            return '';
768
        }
769
770
        if (empty($imageSize)) {
771
            $imageSize = 'img_small';
772
        } else {
773
            $imageSize = "img_$imageSize";
774
        }
775
776
        $html = '';
777
        if ($addDivWrapper) {
778
            $html = '<div class="scrollbar-inner badges-sidebar">';
779
        }
780
        $html .= '<ul class="list-unstyled list-badges">';
781
        foreach ($skills as $skill) {
782
            if (isset($skill['data'])) {
783
                $skill = $skill['data'];
784
            }
785
            $html .= '<li class="thumbnail">';
786
            $item = $skill[$imageSize];
787
            $item .= '<div class="caption">
788
                        <p class="text-center">'.$skill['name'].'</p>
789
                      </div>';
790
            if (isset($skill['url'])) {
791
                $html .= Display::url($item, $skill['url'], ['target' => '_blank']);
792
            } else {
793
                $html .= $item;
794
            }
795
            $html .= '</li>';
796
        }
797
        $html .= '</ul>';
798
799
        if ($addDivWrapper) {
800
            $html .= '</div>';
801
        }
802
803
        return $html;
804
    }
805
806
    /**
807
     * @param $skills
808
     * @param string $imageSize mini|small|big
809
     * @param string $style
810
     * @param bool   $showBadge
811
     * @param bool   $showTitle
812
     *
813
     * @return string
814
     */
815
    public function processSkillListSimple($skills, $imageSize = '', $style = '', $showBadge = true, $showTitle = true)
816
    {
817
        if (empty($skills)) {
818
            return '';
819
        }
820
821
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
822
823
        if (empty($imageSize)) {
824
            $imageSize = 'img_small';
825
        } else {
826
            $imageSize = "img_$imageSize";
827
        }
828
829
        $html = '';
830
        foreach ($skills as $skill) {
831
            if (isset($skill['data'])) {
832
                $skill = $skill['data'];
833
            }
834
835
            $item = '';
836
            if ($showBadge) {
837
                $item = '<div class="item">'.$skill[$imageSize].'</div>';
838
            }
839
840
            $name = '<div class="caption">'.$skill['name'].'</div>';
841
            if (!empty($skill['short_code'])) {
842
                $name = $skill['short_code'];
843
            }
844
845
            if (!$isHierarchicalTable) {
846
                //$item .= '<br />';
847
            }
848
849
            if ($showTitle) {
850
                $item .= $name;
851
            }
852
853
            if (isset($skill['url'])) {
854
                $html .= Display::url($item, $skill['url'], ['target' => '_blank', 'style' => $style]);
855
            } else {
856
                $html .= Display::url($item, '#', ['target' => '_blank', 'style' => $style]);
857
            }
858
        }
859
860
        return $html;
861
    }
862
863
    /**
864
     * @param int $id
865
     *
866
     * @return array
867
     */
868
    public function getSkillInfo($id)
869
    {
870
        $skillRelSkill = new SkillRelSkill();
871
        $skillInfo = $this->get($id);
872
        if (!empty($skillInfo)) {
873
            $skillInfo['extra'] = $skillRelSkill->getSkillInfo($id);
874
            $skillInfo['gradebooks'] = $this->getGradebooksBySkill($id);
875
        }
876
877
        return $skillInfo;
878
    }
879
880
    /**
881
     * @param array $skill_list
882
     *
883
     * @return array
884
     */
885
    public function getSkillsInfo($skill_list)
886
    {
887
        $skill_list = array_map('intval', $skill_list);
888
        $skill_list = implode("', '", $skill_list);
889
890
        $sql = "SELECT * FROM {$this->table}
891
                WHERE id IN ('$skill_list') ";
892
893
        $result = Database::query($sql);
894
        $skills = Database::store_result($result, 'ASSOC');
895
896
        foreach ($skills as &$skill) {
897
            if (!$skill['icon']) {
898
                continue;
899
            }
900
901
            $skill['icon_small'] = sprintf(
902
                'badges/%s-small.png',
903
                sha1($skill['name'])
904
            );
905
            $skill['name'] = self::translateName($skill['name']);
906
            $skill['short_code'] = self::translateCode($skill['short_code']);
907
        }
908
909
        return $skills;
910
    }
911
912
    /**
913
     * @param bool $load_user_data
914
     * @param bool $user_id
915
     * @param int  $id
916
     * @param int  $parent_id
917
     *
918
     * @return array
919
     */
920
    public function get_all(
921
        $load_user_data = false,
922
        $user_id = false,
923
        $id = null,
924
        $parent_id = null
925
    ) {
926
        $id_condition = '';
927
        if (!empty($id)) {
928
            $id = (int) $id;
929
            $id_condition = " WHERE s.id = $id";
930
        }
931
932
        if (!empty($parent_id)) {
933
            $parent_id = (int) $parent_id;
934
            if (empty($id_condition)) {
935
                $id_condition = " WHERE ss.parent_id = $parent_id";
936
            } else {
937
                $id_condition = " AND ss.parent_id = $parent_id";
938
            }
939
        }
940
941
        $sql = "SELECT
942
                    s.id,
943
                    s.name,
944
                    s.description,
945
                    ss.parent_id,
946
                    ss.relation_type,
947
                    s.icon,
948
                    s.short_code,
949
                    s.status
950
                FROM {$this->table} s
951
                INNER JOIN {$this->table_skill_rel_skill} ss
952
                ON (s.id = ss.skill_id) $id_condition
953
                ORDER BY ss.id, ss.parent_id";
954
955
        $result = Database::query($sql);
956
        $skills = [];
957
        $webPath = api_get_path(WEB_UPLOAD_PATH);
958
        if (Database::num_rows($result)) {
959
            while ($row = Database::fetch_array($result, 'ASSOC')) {
960
                $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

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

979
                /** @scrutinizer ignore-call */ 
980
                $children = self::get_all($load_user_data, $user_id, $id, $skill['id']);
Loading history...
980
                if (!empty($children)) {
981
                    //$skills = array_merge($skills, $children);
982
                    $skills = $skills + $children;
983
                }
984
            }
985
        }
986
987
        return $skills;
988
    }
989
990
    /**
991
     * @param int $skill_id
992
     *
993
     * @return array|resource
994
     */
995
    public function getGradebooksBySkill($skill_id)
996
    {
997
        $skill_id = (int) $skill_id;
998
        $sql = "SELECT g.* FROM {$this->table_gradebook} g
999
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1000
                ON g.id = sg.gradebook_id
1001
                WHERE sg.skill_id = $skill_id";
1002
        $result = Database::query($sql);
1003
        $result = Database::store_result($result, 'ASSOC');
1004
1005
        return $result;
1006
    }
1007
1008
    /**
1009
     * Get one level children.
1010
     *
1011
     * @param int  $skill_id
1012
     * @param bool $load_user_data
1013
     *
1014
     * @return array
1015
     */
1016
    public function getChildren($skill_id, $load_user_data = false)
1017
    {
1018
        $skillRelSkill = new SkillRelSkill();
1019
        if ($load_user_data) {
1020
            $user_id = api_get_user_id();
1021
            $skills = $skillRelSkill->getChildren($skill_id, true, $user_id);
1022
        } else {
1023
            $skills = $skillRelSkill->getChildren($skill_id);
1024
        }
1025
1026
        return $skills;
1027
    }
1028
1029
    /**
1030
     * Get all children of the current node (recursive).
1031
     *
1032
     * @param int $skillId
1033
     *
1034
     * @return array
1035
     */
1036
    public function getAllChildren($skillId)
1037
    {
1038
        $skillRelSkill = new SkillRelSkill();
1039
        $children = $skillRelSkill->getChildren($skillId);
1040
        foreach ($children as $child) {
1041
            $subChildren = $this->getAllChildren($child['id']);
1042
        }
1043
1044
        if (!empty($subChildren)) {
1045
            $children = array_merge($children, $subChildren);
1046
        }
1047
1048
        return $children;
1049
    }
1050
1051
    /**
1052
     * Gets all parents from from the wanted skill.
1053
     */
1054
    public function get_parents($skillId)
1055
    {
1056
        $skillRelSkill = new SkillRelSkill();
1057
        $skills = $skillRelSkill->getSkillParents($skillId, true);
1058
        foreach ($skills as &$skill) {
1059
            $skill['data'] = $this->get($skill['skill_id']);
1060
        }
1061
1062
        return $skills;
1063
    }
1064
1065
    /**
1066
     * All direct parents.
1067
     *
1068
     * @param int $skillId
1069
     *
1070
     * @return array
1071
     */
1072
    public function getDirectParents($skillId)
1073
    {
1074
        $skillRelSkill = new SkillRelSkill();
1075
        $skills = $skillRelSkill->getDirectParents($skillId, true);
1076
        if (!empty($skills)) {
1077
            foreach ($skills as &$skill) {
1078
                $skillData = $this->get($skill['skill_id']);
1079
                if (empty($skillData)) {
1080
                    continue;
1081
                }
1082
                $skill['data'] = $skillData;
1083
                $skill_info2 = $skillRelSkill->getSkillInfo($skill['skill_id']);
1084
                $parentId = isset($skill_info2['parent_id']) ? isset($skill_info2['parent_id']) : 0;
1085
                $skill['data']['parent_id'] = $parentId;
1086
            }
1087
1088
            return $skills;
1089
        }
1090
1091
        return [];
1092
    }
1093
1094
    /**
1095
     * Adds a new skill.
1096
     *
1097
     * @param array $params
1098
     *
1099
     * @return bool|null
1100
     */
1101
    public function add($params)
1102
    {
1103
        if (!isset($params['parent_id'])) {
1104
            $params['parent_id'] = 1;
1105
        }
1106
1107
        if (!is_array($params['parent_id'])) {
1108
            $params['parent_id'] = [$params['parent_id']];
1109
        }
1110
1111
        $skillRelSkill = new SkillRelSkill();
1112
        $skillRelGradebook = new SkillRelGradebook();
1113
1114
        // Saving name, description
1115
        $skill_id = $this->save($params);
1116
        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...
1117
            // Saving skill_rel_skill (parent_id, relation_type)
1118
            foreach ($params['parent_id'] as $parent_id) {
1119
                $relation_exists = $skillRelSkill->relationExists($skill_id, $parent_id);
1120
                if (!$relation_exists) {
1121
                    $attributes = [
1122
                        'skill_id' => $skill_id,
1123
                        'parent_id' => $parent_id,
1124
                        'relation_type' => isset($params['relation_type']) ? $params['relation_type'] : 0,
1125
                        //'level'         => $params['level'],
1126
                    ];
1127
                    $skillRelSkill->save($attributes);
1128
                }
1129
            }
1130
1131
            if (!empty($params['gradebook_id'])) {
1132
                foreach ($params['gradebook_id'] as $gradebook_id) {
1133
                    $attributes = [];
1134
                    $attributes['gradebook_id'] = $gradebook_id;
1135
                    $attributes['skill_id'] = $skill_id;
1136
                    $skillRelGradebook->save($attributes);
1137
                }
1138
            }
1139
1140
            return $skill_id;
1141
        }
1142
1143
        return null;
1144
    }
1145
1146
    /**
1147
     * @param int      $userId
1148
     * @param Category $category
1149
     * @param int      $courseId
1150
     * @param int      $sessionId
1151
     *
1152
     * @return bool
1153
     */
1154
    public function addSkillToUser(
1155
        $userId,
1156
        $category,
1157
        $courseId,
1158
        $sessionId
1159
    ) {
1160
        $skill_gradebook = new SkillRelGradebook();
1161
        $skill_rel_user = new SkillRelUser();
1162
1163
        if (empty($category)) {
1164
            return false;
1165
        }
1166
1167
        // Load subcategories
1168
        if (empty($category->get_parent_id())) {
1169
            $subCategories = $category->get_subcategories(
1170
                $userId,
1171
                $category->get_course_code(),
1172
                $category->get_session_id()
1173
            );
1174
            if (!empty($subCategories)) {
1175
                /** @var Category $subCategory */
1176
                foreach ($subCategories as $subCategory) {
1177
                    $this->addSkillToUser($userId, $subCategory, $courseId, $sessionId);
1178
                }
1179
            }
1180
        }
1181
1182
        $gradebookId = $category->get_id();
1183
        $skill_gradebooks = $skill_gradebook->get_all(['where' => ['gradebook_id = ?' => $gradebookId]]);
1184
1185
        if (!empty($skill_gradebooks)) {
1186
            foreach ($skill_gradebooks as $skill_gradebook) {
1187
                $hasSkill = $this->userHasSkill(
1188
                    $userId,
1189
                    $skill_gradebook['skill_id'],
1190
                    $courseId,
1191
                    $sessionId
1192
                );
1193
1194
                if (!$hasSkill) {
1195
                    $params = [
1196
                        'user_id' => $userId,
1197
                        'skill_id' => $skill_gradebook['skill_id'],
1198
                        'acquired_skill_at' => api_get_utc_datetime(),
1199
                        'course_id' => (int) $courseId,
1200
                        'session_id' => $sessionId ? (int) $sessionId : null,
1201
                    ];
1202
1203
                    $skill_rel_user->save($params);
1204
                }
1205
            }
1206
        }
1207
1208
        return true;
1209
    }
1210
1211
    /* Deletes a skill */
1212
    public function delete($skill_id)
1213
    {
1214
        /*$params = array('skill_id' => $skill_id);
1215
1216
        $skillRelSkill     = new SkillRelSkill();
1217
        $skills = $skillRelSkill->get_all(array('where'=>array('skill_id = ?' =>$skill_id)));
1218
1219
        $skill_rel_profile     = new SkillRelProfile();
1220
        $skillRelGradebook = new SkillRelGradebook();
1221
        $skill_rel_user     = new SkillRelUser();
1222
1223
        $this->delete($skill_id);
1224
1225
        $skillRelGradebook->delete($params);*/
1226
    }
1227
1228
    /**
1229
     * @param array $params
1230
     */
1231
    public function edit($params)
1232
    {
1233
        if (!isset($params['parent_id'])) {
1234
            $params['parent_id'] = 1;
1235
        }
1236
1237
        $params['gradebook_id'] = isset($params['gradebook_id']) ? $params['gradebook_id'] : [];
1238
1239
        $skillRelSkill = new SkillRelSkill();
1240
        $skillRelGradebook = new SkillRelGradebook();
1241
1242
        // Saving name, description
1243
        $this->update($params);
1244
        $skillId = $params['id'];
1245
1246
        if ($skillId) {
1247
            // Saving skill_rel_skill (parent_id, relation_type)
1248
            if (!is_array($params['parent_id'])) {
1249
                $params['parent_id'] = [$params['parent_id']];
1250
            }
1251
1252
            // Cannot change parent of root
1253
            if ($skillId == 1) {
1254
                $params['parent_id'] = 0;
1255
            }
1256
1257
            foreach ($params['parent_id'] as $parent_id) {
1258
                $relation_exists = $skillRelSkill->relationExists($skillId, $parent_id);
1259
                if (!$relation_exists) {
1260
                    $attributes = [
1261
                        'skill_id' => $skillId,
1262
                        'parent_id' => $parent_id,
1263
                        'relation_type' => $params['relation_type'],
1264
                        //'level'         => $params['level'],
1265
                    ];
1266
                    $skillRelSkill->updateBySkill($attributes);
1267
                }
1268
            }
1269
1270
            $skillRelGradebook->updateGradeBookListBySkill(
1271
                $skillId,
1272
                $params['gradebook_id']
1273
            );
1274
1275
            return $skillId;
1276
        }
1277
1278
        return null;
1279
    }
1280
1281
    /**
1282
     * Get user's skills.
1283
     *
1284
     * @param int  $userId
1285
     * @param bool $getSkillData
1286
     * @param int  $courseId
1287
     * @param int  $sessionId
1288
     *
1289
     * @return array
1290
     */
1291
    public function getUserSkills($userId, $getSkillData = false, $courseId = 0, $sessionId = 0)
1292
    {
1293
        $userId = (int) $userId;
1294
        $courseId = (int) $courseId;
1295
        $sessionId = (int) $sessionId;
1296
1297
        $courseCondition = '';
1298
        if (!empty($courseId)) {
1299
            $courseCondition = " AND course_id = $courseId ";
1300
        }
1301
1302
        $sessionCondition = '';
1303
        if (!empty($sessionId)) {
1304
            $sessionCondition = " AND course_id = $sessionId ";
1305
        }
1306
1307
        $sql = 'SELECT DISTINCT
1308
                    s.id,
1309
                    s.name,
1310
                    s.icon,
1311
                    u.id as issue,
1312
                    u.acquired_skill_at,
1313
                    u.course_id
1314
                FROM '.$this->table_skill_rel_user.' u
1315
                INNER JOIN '.$this->table.' s
1316
                ON u.skill_id = s.id
1317
                WHERE
1318
                    user_id = '.$userId.' '.$sessionCondition.' '.$courseCondition;
1319
1320
        $result = Database::query($sql);
1321
        $skills = Database::store_result($result, 'ASSOC');
1322
        $skillList = [];
1323
        if (!empty($skills)) {
1324
            foreach ($skills as $skill) {
1325
                if ($getSkillData) {
1326
                    $skillData = $this->get($skill['id']);
1327
                    $skillData['url'] = api_get_path(WEB_PATH).'badge/'.$skill['id'].'/user/'.$userId;
1328
                    $skillList[$skill['id']] = array_merge($skill, $skillData);
1329
                } else {
1330
                    $skillList[$skill['id']] = $skill['id'];
1331
                }
1332
            }
1333
        }
1334
1335
        return $skillList;
1336
    }
1337
1338
    /**
1339
     * @param array $skills
1340
     * @param int   $level
1341
     *
1342
     * @return string
1343
     */
1344
    public function processVertex(Vertex $vertex, $skills = [], $level = 0)
1345
    {
1346
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
1347
        $subTable = '';
1348
        if ($vertex->getVerticesEdgeTo()->count() > 0) {
1349
            if ($isHierarchicalTable) {
1350
                $subTable .= '<ul>';
1351
            }
1352
            foreach ($vertex->getVerticesEdgeTo() as $subVertex) {
1353
                $data = $subVertex->getAttribute('graphviz.data');
1354
                $passed = in_array($data['id'], array_keys($skills));
1355
                $transparency = '';
1356
                if ($passed === false) {
1357
                    // @todo use css class
1358
                    $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1359
                }
1360
1361
                if ($isHierarchicalTable) {
1362
                    $label = $this->processSkillListSimple([$data], 'mini', $transparency);
1363
                    $subTable .= '<li>'.$label;
1364
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
1365
                    $subTable .= '</li>';
1366
                } else {
1367
                    $imageSize = 'mini';
1368
                    if ($level == 2) {
1369
                        $imageSize = 'small';
1370
                    }
1371
                    $showTitle = true;
1372
                    if ($level > 2) {
1373
                        $showTitle = false;
1374
                    }
1375
1376
                    $label = $this->processSkillListSimple([$data], $imageSize, $transparency, true, $showTitle);
1377
                    $subTable .= '<div class="thumbnail" style="float:left; margin-right:5px; ">';
1378
                    $subTable .= '<div style="'.$transparency.'">';
1379
1380
                    $subTable .= '<div style="text-align: center">';
1381
                    $subTable .= $label;
1382
                    $subTable .= '</div>';
1383
1384
                    $subTable .= '</div>';
1385
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
1386
                    $subTable .= '</div>';
1387
                }
1388
            }
1389
1390
            if ($isHierarchicalTable) {
1391
                $subTable .= '</ul>';
1392
            }
1393
        }
1394
1395
        return $subTable;
1396
    }
1397
1398
    /**
1399
     * @param int  $userId
1400
     * @param int  $courseId
1401
     * @param int  $sessionId
1402
     * @param bool $addTitle
1403
     *
1404
     * @return array
1405
     */
1406
    public function getUserSkillsTable($userId, $courseId = 0, $sessionId = 0, $addTitle = true)
1407
    {
1408
        $skills = $this->getUserSkills($userId, true, $courseId, $sessionId);
1409
        $courseTempList = [];
1410
        $tableRows = [];
1411
        $skillParents = [];
1412
        foreach ($skills as $resultData) {
1413
            $parents = $this->get_parents($resultData['id']);
1414
            foreach ($parents as $parentData) {
1415
                $parentData['passed'] = in_array($parentData['id'], array_keys($skills));
1416
                if ($parentData['passed'] && isset($skills[$parentData['id']]['url'])) {
1417
                    $parentData['data']['url'] = $skills[$parentData['id']]['url'];
1418
                }
1419
                $skillParents[$resultData['id']][$parentData['id']] = $parentData;
1420
            }
1421
        }
1422
1423
        foreach ($skills as $resultData) {
1424
            $courseId = $resultData['course_id'];
1425
            if (!empty($courseId)) {
1426
                if (isset($courseTempList[$courseId])) {
1427
                    $courseInfo = $courseTempList[$courseId];
1428
                } else {
1429
                    $courseInfo = api_get_course_info_by_id($courseId);
1430
                    $courseTempList[$courseId] = $courseInfo;
1431
                }
1432
            } else {
1433
                $courseInfo = [];
1434
            }
1435
            $tableRow = [
1436
                'skill_badge' => $resultData['img_small'],
1437
                'skill_name' => self::translateName($resultData['name']),
1438
                'short_code' => $resultData['short_code'],
1439
                'skill_url' => $resultData['url'],
1440
                'achieved_at' => api_get_local_time($resultData['acquired_skill_at']),
1441
                'course_image' => '',
1442
                'course_name' => '',
1443
            ];
1444
1445
            if (!empty($courseInfo)) {
1446
                $tableRow['course_image'] = $courseInfo['course_image'];
1447
                $tableRow['course_name'] = $courseInfo['title'];
1448
            }
1449
            $tableRows[] = $tableRow;
1450
        }
1451
1452
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
1453
        $allowLevels = api_get_configuration_value('skill_levels_names');
1454
1455
        $tableResult = '<div id="skillList">';
1456
        if ($isHierarchicalTable) {
1457
            $tableResult = '<div class="table-responsive">';
1458
        }
1459
1460
        if ($addTitle) {
1461
            $tableResult .= Display::page_subheader(get_lang('AchievedSkills'));
1462
            $tableResult .= '<div class="skills-badges">';
1463
        }
1464
1465
        if (!empty($skillParents)) {
1466
            if (empty($allowLevels)) {
1467
                $tableResult .= $this->processSkillListSimple($skills);
1468
            } else {
1469
                $graph = new Graph();
1470
                $graph->setAttribute('graphviz.graph.rankdir', 'LR');
1471
                foreach ($skillParents as $skillId => $parentList) {
1472
                    $old = null;
1473
                    foreach ($parentList as $parent) {
1474
                        if ($graph->hasVertex($parent['id'])) {
1475
                            $current = $graph->getVertex($parent['id']);
1476
                        } else {
1477
                            $current = $graph->createVertex($parent['id']);
1478
                            $current->setAttribute('graphviz.data', $parent['data']);
1479
                        }
1480
1481
                        if (!empty($old)) {
1482
                            if ($graph->hasVertex($old['id'])) {
1483
                                $nextVertex = $graph->getVertex($old['id']);
1484
                            } else {
1485
                                $nextVertex = $graph->createVertex($old['id']);
1486
                                $nextVertex->setAttribute('graphviz.data', $old['data']);
1487
                            }
1488
1489
                            if (!$nextVertex->hasEdgeTo($current)) {
1490
                                $nextVertex->createEdgeTo($current);
1491
                            }
1492
                        }
1493
                        $old = $parent;
1494
                    }
1495
                }
1496
1497
                if ($isHierarchicalTable) {
1498
                    $table = '<table class ="table table-bordered">';
1499
                    // Getting "root" vertex
1500
                    $root = $graph->getVertex(1);
1501
                    $table .= '<tr>';
1502
                    /** @var Vertex $vertex */
1503
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
1504
                        $data = $vertex->getAttribute('graphviz.data');
1505
1506
                        $passed = in_array($data['id'], array_keys($skills));
1507
                        $transparency = '';
1508
                        if ($passed === false) {
1509
                            // @todo use a css class
1510
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1511
                        }
1512
1513
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency);
1514
                        $table .= '<td >';
1515
1516
                        $table .= '<div class="skills_chart"> <ul><li>'.$label;
1517
                        $table .= $this->processVertex($vertex, $skills);
1518
                        $table .= '</ul></li></div>';
1519
                        $table .= '</td>';
1520
                    }
1521
                    $table .= '</tr></table>';
1522
                } else {
1523
                    // Getting "root" vertex
1524
                    $root = $graph->getVertex(1);
1525
                    $table = '';
1526
                    /** @var Vertex $vertex */
1527
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
1528
                        $data = $vertex->getAttribute('graphviz.data');
1529
1530
                        $passed = in_array($data['id'], array_keys($skills));
1531
                        $transparency = '';
1532
                        if ($passed === false) {
1533
                            // @todo use a css class
1534
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1535
                        }
1536
1537
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency, false);
1538
1539
                        $skillTable = $this->processVertex($vertex, $skills, 2);
1540
                        $table .= "<h3>$label</h3>";
1541
1542
                        if (!empty($skillTable)) {
1543
                            $table .= '<table class ="table table-bordered">';
1544
                            $table .= '<tr>';
1545
                            $table .= '<td>';
1546
                            $table .= '<div>';
1547
                            $table .= $skillTable;
1548
                            $table .= '</div>';
1549
                            $table .= '</td>';
1550
                            $table .= '</tr></table>';
1551
                        }
1552
                    }
1553
                }
1554
1555
                $tableResult .= $table;
1556
            }
1557
        } else {
1558
            $tableResult .= get_lang('WithoutAchievedSkills');
1559
        }
1560
1561
        if ($addTitle) {
1562
            $tableResult .= '</div>';
1563
        }
1564
        $tableResult .= '</div>';
1565
1566
        return [
1567
            'skills' => $tableRows,
1568
            'table' => $tableResult,
1569
        ];
1570
    }
1571
1572
    /**
1573
     * @param int  $user_id
1574
     * @param int  $skill_id
1575
     * @param bool $return_flat_array
1576
     * @param bool $add_root
1577
     *
1578
     * @return array|null
1579
     */
1580
    public function getSkillsTree(
1581
        $user_id = null,
1582
        $skill_id = null,
1583
        $return_flat_array = false,
1584
        $add_root = false
1585
    ) {
1586
        if ($skill_id == 1) {
1587
            $skill_id = 0;
1588
        }
1589
        if (isset($user_id) && !empty($user_id)) {
1590
            $skills = $this->get_all(true, $user_id, null, $skill_id);
1591
        } else {
1592
            $skills = $this->get_all(false, false, null, $skill_id);
1593
        }
1594
1595
        $original_skill = $this->list = $skills;
1596
1597
        // Show 1 item
1598
        if (!empty($skill_id)) {
1599
            if ($add_root) {
1600
                if (!empty($skill_id)) {
1601
                    // Default root node
1602
                    $skills[1] = [
1603
                        'id' => '1',
1604
                        'name' => get_lang('Root'),
1605
                        'parent_id' => '0',
1606
                        'status' => 1,
1607
                    ];
1608
                    $skillInfo = $this->getSkillInfo($skill_id);
1609
1610
                    // 2nd node
1611
                    $skills[$skill_id] = $skillInfo;
1612
                    // Uncomment code below to hide the searched skill
1613
                    $skills[$skill_id]['data']['parent_id'] = $skillInfo['extra']['parent_id'];
1614
                    $skills[$skill_id]['parent_id'] = 1;
1615
                }
1616
            }
1617
        }
1618
1619
        $refs = [];
1620
        $skills_tree = null;
1621
1622
        // Create references for all nodes
1623
        $flat_array = [];
1624
        $family = [];
1625
        if (!empty($skills)) {
1626
            foreach ($skills as &$skill) {
1627
                if ($skill['parent_id'] == 0) {
1628
                    $skill['parent_id'] = 'root';
1629
                }
1630
1631
                // because except main keys (id, name, children) others keys
1632
                // are not saved while in the space tree
1633
                $skill['data'] = ['parent_id' => $skill['parent_id']];
1634
1635
                // If a short code was defined, send the short code to replace
1636
                // skill name (to shorten the text in the wheel)
1637
                if (!empty($skill['short_code']) &&
1638
                    api_get_setting('show_full_skill_name_on_skill_wheel') === 'false'
1639
                ) {
1640
                    $skill['data']['short_code'] = $skill['short_code'];
1641
                }
1642
1643
                $skill['data']['name'] = $skill['name'];
1644
                $skill['data']['status'] = $skill['status'];
1645
1646
                // In order to paint all members of a family with the same color
1647
                if (empty($skill_id)) {
1648
                    if ($skill['parent_id'] == 1) {
1649
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1650
                    }
1651
                } else {
1652
                    if ($skill['parent_id'] == $skill_id) {
1653
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1654
                    }
1655
                    /*if ($skill_id == $skill['id']) {
1656
                        $skill['parent_id'] = 1;
1657
                    }*/
1658
                }
1659
1660
                if (!isset($skill['data']['real_parent_id'])) {
1661
                    $skill['data']['real_parent_id'] = $skill['parent_id'];
1662
                }
1663
1664
                // User achieved the skill (depends in the gradebook with certification)
1665
                $skill['data']['achieved'] = false;
1666
                if ($user_id) {
1667
                    $skill['data']['achieved'] = $this->userHasSkill(
1668
                        $user_id,
1669
                        $skill['id']
1670
                    );
1671
                }
1672
1673
                // Check if the skill has related gradebooks
1674
                $skill['data']['skill_has_gradebook'] = false;
1675
                if (isset($skill['gradebooks']) && !empty($skill['gradebooks'])) {
1676
                    $skill['data']['skill_has_gradebook'] = true;
1677
                }
1678
                $refs[$skill['id']] = &$skill;
1679
                $flat_array[$skill['id']] = &$skill;
1680
            }
1681
1682
            // Checking family value
1683
1684
            $family_id = 1;
1685
            $new_family_array = [];
1686
            foreach ($family as $main_family_id => $family_items) {
1687
                if (!empty($family_items)) {
1688
                    foreach ($family_items as $item) {
1689
                        $new_family_array[$item['id']] = $family_id;
1690
                    }
1691
                }
1692
                $new_family_array[$main_family_id] = $family_id;
1693
                $family_id++;
1694
            }
1695
1696
            if (empty($original_skill)) {
1697
                $refs['root']['children'][0] = $skills[1];
1698
                $skills[$skill_id]['data']['family_id'] = 1;
1699
                $refs['root']['children'][0]['children'][0] = $skills[$skill_id];
1700
                $flat_array[$skill_id] = $skills[$skill_id];
1701
            } else {
1702
                // Moving node to the children index of their parents
1703
                foreach ($skills as $my_skill_id => &$skill) {
1704
                    if (isset($new_family_array[$skill['id']])) {
1705
                        $skill['data']['family_id'] = $new_family_array[$skill['id']];
1706
                    }
1707
                    $refs[$skill['parent_id']]['children'][] = &$skill;
1708
                    $flat_array[$my_skill_id] = $skill;
1709
                }
1710
            }
1711
1712
            $skills_tree = [
1713
                'name' => get_lang('SkillRootName'),
1714
                'id' => 'root',
1715
                'children' => $refs['root']['children'],
1716
                'data' => [],
1717
            ];
1718
        }
1719
1720
        if ($return_flat_array) {
1721
            return $flat_array;
1722
        }
1723
        unset($skills);
1724
1725
        return $skills_tree;
1726
    }
1727
1728
    /**
1729
     * Get skills tree as a simplified JSON structure.
1730
     *
1731
     * @param int user id
1732
     * @param int skill id
1733
     * @param bool return a flat array or not
1734
     * @param int depth of the skills
1735
     *
1736
     * @return string json
1737
     */
1738
    public function getSkillsTreeToJson(
1739
        $user_id = null,
1740
        $skill_id = null,
1741
        $return_flat_array = false,
1742
        $main_depth = 2
1743
    ) {
1744
        $tree = $this->getSkillsTree(
1745
            $user_id,
1746
            $skill_id,
1747
            $return_flat_array,
1748
            true
1749
        );
1750
        $simple_tree = [];
1751
        if (!empty($tree['children'])) {
1752
            foreach ($tree['children'] as $element) {
1753
                $children = [];
1754
                if (isset($element['children'])) {
1755
                    $children = $this->getSkillToJson($element['children'], 1, $main_depth);
1756
                }
1757
                $simple_tree[] = [
1758
                    'name' => $element['name'],
1759
                    'children' => $children,
1760
                ];
1761
            }
1762
        }
1763
1764
        return json_encode($simple_tree[0]['children']);
1765
    }
1766
1767
    /**
1768
     * Get JSON element.
1769
     *
1770
     * @param array $subtree
1771
     * @param int   $depth
1772
     * @param int   $max_depth
1773
     *
1774
     * @return array|null
1775
     */
1776
    public function getSkillToJson($subtree, $depth = 1, $max_depth = 2)
1777
    {
1778
        $simple_sub_tree = [];
1779
        if (is_array($subtree)) {
1780
            $counter = 1;
1781
            foreach ($subtree as $elem) {
1782
                $tmp = [];
1783
                $tmp['name'] = $elem['name'];
1784
                $tmp['id'] = $elem['id'];
1785
                $tmp['isSearched'] = self::isSearched($elem['id']);
1786
1787
                if (isset($elem['children']) && is_array($elem['children'])) {
1788
                    $tmp['children'] = $this->getSkillToJson(
1789
                        $elem['children'],
1790
                        $depth + 1,
1791
                        $max_depth
1792
                    );
1793
                }
1794
1795
                if ($depth > $max_depth) {
1796
                    continue;
1797
                }
1798
1799
                $tmp['depth'] = $depth;
1800
                $tmp['counter'] = $counter;
1801
                $counter++;
1802
1803
                if (isset($elem['data']) && is_array($elem['data'])) {
1804
                    foreach ($elem['data'] as $key => $item) {
1805
                        $tmp[$key] = $item;
1806
                    }
1807
                }
1808
                $simple_sub_tree[] = $tmp;
1809
            }
1810
1811
            return $simple_sub_tree;
1812
        }
1813
1814
        return null;
1815
    }
1816
1817
    /**
1818
     * @param int $user_id
1819
     *
1820
     * @return bool
1821
     */
1822
    public function getUserSkillRanking($user_id)
1823
    {
1824
        $user_id = (int) $user_id;
1825
        $sql = "SELECT count(skill_id) count
1826
                FROM {$this->table} s
1827
                INNER JOIN {$this->table_skill_rel_user} su
1828
                ON (s.id = su.skill_id)
1829
                WHERE user_id = $user_id";
1830
        $result = Database::query($sql);
1831
        if (Database::num_rows($result)) {
1832
            $result = Database::fetch_row($result);
1833
1834
            return $result[0];
1835
        }
1836
1837
        return false;
1838
    }
1839
1840
    /**
1841
     * @param $start
1842
     * @param $limit
1843
     * @param $sidx
1844
     * @param $sord
1845
     * @param $where_condition
1846
     *
1847
     * @return array
1848
     */
1849
    public function getUserListSkillRanking(
1850
        $start,
1851
        $limit,
1852
        $sidx,
1853
        $sord,
1854
        $where_condition
1855
    ) {
1856
        $start = (int) $start;
1857
        $limit = (int) $limit;
1858
1859
        /*  ORDER BY $sidx $sord */
1860
        $sql = "SELECT *, @rownum:=@rownum+1 rank FROM (
1861
                    SELECT u.user_id, firstname, lastname, count(username) skills_acquired
1862
                    FROM {$this->table} s INNER JOIN {$this->table_skill_rel_user} su ON (s.id = su.skill_id)
1863
                    INNER JOIN {$this->table_user} u ON u.user_id = su.user_id, (SELECT @rownum:=0) r
1864
                    WHERE 1=1 $where_condition
1865
                    GROUP BY username
1866
                    ORDER BY skills_acquired desc
1867
                    LIMIT $start , $limit)  AS T1, (SELECT @rownum:=0) r";
1868
        $result = Database::query($sql);
1869
        if (Database::num_rows($result)) {
1870
            return Database::store_result($result, 'ASSOC');
1871
        }
1872
1873
        return [];
1874
    }
1875
1876
    /**
1877
     * @return int
1878
     */
1879
    public function getUserListSkillRankingCount()
1880
    {
1881
        $sql = "SELECT count(*) FROM (
1882
                    SELECT count(distinct 1)
1883
                    FROM {$this->table} s
1884
                    INNER JOIN {$this->table_skill_rel_user} su
1885
                    ON (s.id = su.skill_id)
1886
                    INNER JOIN {$this->table_user} u
1887
                    ON u.user_id = su.user_id
1888
                    GROUP BY username
1889
                 ) as T1";
1890
        $result = Database::query($sql);
1891
        if (Database::num_rows($result)) {
1892
            $result = Database::fetch_row($result);
1893
1894
            return $result[0];
1895
        }
1896
1897
        return 0;
1898
    }
1899
1900
    /**
1901
     * @param string $courseCode
1902
     *
1903
     * @return int
1904
     */
1905
    public function getCountSkillsByCourse($courseCode)
1906
    {
1907
        $courseCode = Database::escape_string($courseCode);
1908
        $sql = "SELECT count(skill_id) as count
1909
                FROM {$this->table_gradebook} g
1910
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1911
                ON g.id = sg.gradebook_id
1912
                WHERE course_code = '$courseCode'";
1913
1914
        $result = Database::query($sql);
1915
        if (Database::num_rows($result)) {
1916
            $result = Database::fetch_row($result);
1917
1918
            return $result[0];
1919
        }
1920
1921
        return 0;
1922
    }
1923
1924
    /**
1925
     * @param int $skillId
1926
     *
1927
     * @return array
1928
     */
1929
    public function getCoursesBySkill($skillId)
1930
    {
1931
        $skillId = (int) $skillId;
1932
        $sql = "SELECT c.title, c.code
1933
                FROM {$this->table_gradebook} g
1934
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1935
                ON g.id = sg.gradebook_id
1936
                INNER JOIN {$this->table_course} c
1937
                ON c.code = g.course_code
1938
                WHERE sg.skill_id = $skillId
1939
                AND (g.session_id IS NULL OR g.session_id = 0)";
1940
        $result = Database::query($sql);
1941
1942
        return Database::store_result($result, 'ASSOC');
1943
    }
1944
1945
    /**
1946
     * Check if the user has the skill.
1947
     *
1948
     * @param int $userId    The user id
1949
     * @param int $skillId   The skill id
1950
     * @param int $courseId  Optional. The course id
1951
     * @param int $sessionId Optional. The session id
1952
     *
1953
     * @return bool Whether the user has the skill return true. Otherwise return false
1954
     */
1955
    public function userHasSkill($userId, $skillId, $courseId = 0, $sessionId = 0)
1956
    {
1957
        $courseId = (int) $courseId;
1958
        $sessionId = (int) $sessionId;
1959
1960
        $whereConditions = [
1961
            'user_id = ? ' => (int) $userId,
1962
            'AND skill_id = ? ' => (int) $skillId,
1963
        ];
1964
1965
        if ($courseId > 0) {
1966
            $whereConditions['AND course_id = ? '] = $courseId;
1967
            $whereConditions['AND session_id = ? '] = $sessionId ? $sessionId : null;
1968
        }
1969
1970
        $result = Database::select(
1971
            'COUNT(1) AS qty',
1972
            $this->table_skill_rel_user,
1973
            [
1974
                'where' => $whereConditions,
1975
            ],
1976
            'first'
1977
        );
1978
1979
        if ($result != false) {
1980
            if ($result['qty'] > 0) {
1981
                return true;
1982
            }
1983
        }
1984
1985
        return false;
1986
    }
1987
1988
    /**
1989
     * Check if a skill is searched.
1990
     *
1991
     * @param int $id The skill id
1992
     *
1993
     * @return bool Whether el skill is searched return true. Otherwise return false
1994
     */
1995
    public static function isSearched($id)
1996
    {
1997
        $id = (int) $id;
1998
1999
        if (empty($id)) {
2000
            return false;
2001
        }
2002
2003
        $skillRelProfileTable = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
2004
2005
        $result = Database::select(
2006
            'COUNT( DISTINCT `skill_id`) AS qty',
2007
            $skillRelProfileTable,
2008
            [
2009
                'where' => [
2010
                    'skill_id = ?' => $id,
2011
                ],
2012
            ],
2013
            'first'
2014
        );
2015
2016
        if ($result === false) {
2017
            return false;
2018
        }
2019
2020
        if ($result['qty'] > 0) {
2021
            return true;
2022
        }
2023
2024
        return false;
2025
    }
2026
2027
    /**
2028
     * Get the achieved skills by course.
2029
     *
2030
     * @param int $courseId The course id
2031
     *
2032
     * @return array The skills list
2033
     */
2034
    public function listAchievedByCourse($courseId)
2035
    {
2036
        $courseId = (int) $courseId;
2037
2038
        if ($courseId == 0) {
2039
            return [];
2040
        }
2041
2042
        $list = [];
2043
2044
        $sql = "SELECT
2045
                    course.id c_id,
2046
                    course.title c_name,
2047
                    course.directory c_directory,
2048
                    user.user_id,
2049
                    user.lastname,
2050
                    user.firstname,
2051
                    user.username,
2052
                    skill.id skill_id,
2053
                    skill.name skill_name,
2054
                    sru.acquired_skill_at
2055
                FROM {$this->table_skill_rel_user} AS sru
2056
                INNER JOIN {$this->table_course}
2057
                ON sru.course_id = course.id
2058
                INNER JOIN {$this->table_user}
2059
                ON sru.user_id = user.user_id
2060
                INNER JOIN {$this->table}
2061
                ON sru.skill_id = skill.id
2062
                WHERE course.id = $courseId";
2063
2064
        $result = Database::query($sql);
2065
2066
        while ($row = Database::fetch_assoc($result)) {
2067
            $row['skill_name'] = self::translateName($row['skill_name']);
2068
            $list[] = $row;
2069
        }
2070
2071
        return $list;
2072
    }
2073
2074
    /**
2075
     * Get the users list who achieved a skill.
2076
     *
2077
     * @param int $skillId The skill id
2078
     *
2079
     * @return array The users list
2080
     */
2081
    public function listUsersWhoAchieved($skillId)
2082
    {
2083
        $skillId = (int) $skillId;
2084
2085
        if ($skillId == 0) {
2086
            return [];
2087
        }
2088
2089
        $list = [];
2090
        $sql = "SELECT
2091
                    course.id c_id,
2092
                    course.title c_name,
2093
                    course.directory c_directory,
2094
                    user.user_id,
2095
                    user.lastname,
2096
                    user.firstname,
2097
                    user.username,
2098
                    skill.id skill_id,
2099
                    skill.name skill_name,
2100
                    sru.acquired_skill_at
2101
                FROM {$this->table_skill_rel_user} AS sru
2102
                INNER JOIN {$this->table_course}
2103
                ON sru.course_id = course.id
2104
                INNER JOIN {$this->table_user}
2105
                ON sru.user_id = user.user_id
2106
                INNER JOIN {$this->table}
2107
                ON sru.skill_id = skill.id
2108
                WHERE skill.id = $skillId ";
2109
2110
        $result = Database::query($sql);
2111
        while ($row = Database::fetch_assoc($result)) {
2112
            $row['skill_name'] = self::translateName($row['skill_name']);
2113
            $list[] = $row;
2114
        }
2115
2116
        return $list;
2117
    }
2118
2119
    /**
2120
     * Get the session list where the user can achieve a skill.
2121
     *
2122
     * @param int $skillId The skill id
2123
     *
2124
     * @return array
2125
     */
2126
    public function getSessionsBySkill($skillId)
2127
    {
2128
        $skillId = (int) $skillId;
2129
2130
        $sql = "SELECT s.id, s.name
2131
                FROM {$this->table_gradebook} g
2132
                INNER JOIN {$this->table_skill_rel_gradebook} sg
2133
                ON g.id = sg.gradebook_id
2134
                INNER JOIN {$this->sessionTable} s
2135
                ON g.session_id = s.id
2136
                WHERE sg.skill_id = $skillId
2137
                AND g.session_id > 0";
2138
2139
        $result = Database::query($sql);
2140
2141
        return Database::store_result($result, 'ASSOC');
2142
    }
2143
2144
    /**
2145
     * Check if the $fromUser can comment the $toUser skill issue.
2146
     *
2147
     * @param User $fromUser
2148
     * @param User $toUser
2149
     *
2150
     * @return bool
2151
     */
2152
    public static function userCanAddFeedbackToUser($fromUser, $toUser)
2153
    {
2154
        if (api_is_platform_admin()) {
2155
            return true;
2156
        }
2157
2158
        $userRepo = UserManager::getRepository();
2159
        $fromUserStatus = $fromUser->getStatus();
2160
2161
        switch ($fromUserStatus) {
2162
            case SESSIONADMIN:
2163
                if (api_get_setting('allow_session_admins_to_manage_all_sessions') === 'true') {
2164
                    if ($toUser->getCreatorId() === $fromUser->getId()) {
2165
                        return true;
2166
                    }
2167
                }
2168
2169
                $sessionAdmins = $userRepo->getSessionAdmins($toUser);
2170
2171
                foreach ($sessionAdmins as $sessionAdmin) {
2172
                    if ($sessionAdmin->getId() !== $fromUser->getId()) {
2173
                        continue;
2174
                    }
2175
2176
                    return true;
2177
                }
2178
                break;
2179
            case STUDENT_BOSS:
2180
                $studentBosses = $userRepo->getStudentBosses($toUser);
2181
                foreach ($studentBosses as $studentBoss) {
2182
                    if ($studentBoss->getId() !== $fromUser->getId()) {
2183
                        continue;
2184
                    }
2185
2186
                    return true;
2187
                }
2188
                break;
2189
            case DRH:
2190
                return UserManager::is_user_followed_by_drh(
2191
                    $toUser->getId(),
2192
                    $fromUser->getId()
2193
                );
2194
        }
2195
2196
        return false;
2197
    }
2198
2199
    /**
2200
     * If $studentId is set then check if current user has the right to see
2201
     * the page.
2202
     *
2203
     * @param int  $studentId check if current user has access to see $studentId
2204
     * @param bool $blockPage raise a api_not_allowed()
2205
     *
2206
     * @return bool
2207
     */
2208
    public static function isAllowed($studentId = 0, $blockPage = true)
2209
    {
2210
        $allowHR = api_get_setting('allow_hr_skills_management') === 'true';
2211
2212
        if (self::isToolAvailable()) {
2213
            if (api_is_platform_admin(false, $allowHR)) {
2214
                return true;
2215
            }
2216
2217
            if (!empty($studentId)) {
2218
                $currentUserId = api_get_user_id();
2219
                if ((int) $currentUserId === (int) $studentId) {
2220
                    return true;
2221
                }
2222
2223
                $haveAccess = self::hasAccessToUserSkill(
2224
                    $currentUserId,
2225
                    $studentId
2226
                );
2227
2228
                if ($haveAccess) {
2229
                    return true;
2230
                }
2231
            }
2232
        }
2233
2234
        if ($blockPage) {
2235
            api_not_allowed(true);
2236
        }
2237
2238
        return false;
2239
    }
2240
2241
    /**
2242
     * @return bool
2243
     */
2244
    public static function isToolAvailable()
2245
    {
2246
        $allowTool = api_get_setting('allow_skills_tool');
2247
2248
        if ($allowTool === 'true') {
2249
            return true;
2250
        }
2251
2252
        return false;
2253
    }
2254
2255
    /**
2256
     * @param int $currentUserId
2257
     * @param int $studentId
2258
     *
2259
     * @return bool
2260
     */
2261
    public static function hasAccessToUserSkill($currentUserId, $studentId)
2262
    {
2263
        if (self::isToolAvailable()) {
2264
            if (api_is_platform_admin()) {
2265
                return true;
2266
            }
2267
2268
            $currentUserId = (int) $currentUserId;
2269
            $studentId = (int) $studentId;
2270
2271
            if ($currentUserId === $studentId) {
2272
                return true;
2273
            }
2274
2275
            if (api_is_student_boss()) {
2276
                $isBoss = UserManager::userIsBossOfStudent($currentUserId, $studentId);
2277
                if ($isBoss) {
2278
                    return true;
2279
                }
2280
            }
2281
2282
            $allow = api_get_configuration_value('allow_private_skills');
2283
            if ($allow === true) {
2284
                if (api_is_teacher()) {
2285
                    return UserManager::isTeacherOfStudent(
2286
                        $currentUserId,
2287
                        $studentId
2288
                    );
2289
                }
2290
2291
                if (api_is_drh()) {
2292
                    return UserManager::is_user_followed_by_drh(
2293
                        $studentId,
2294
                        $currentUserId
2295
                    );
2296
                }
2297
            }
2298
        }
2299
2300
        return false;
2301
    }
2302
2303
    /**
2304
     * Get skills.
2305
     *
2306
     * @param int $userId
2307
     * @param int level
2308
     *
2309
     * @return array
2310
     */
2311
    public function getStudentSkills($userId, $level = 0)
2312
    {
2313
        $userId = (int) $userId;
2314
2315
        $sql = "SELECT s.id, s.name, sru.acquired_skill_at
2316
                FROM {$this->table} s
2317
                INNER JOIN {$this->table_skill_rel_user} sru
2318
                ON s.id = sru.skill_id
2319
                WHERE sru.user_id = $userId";
2320
2321
        $result = Database::query($sql);
2322
2323
        $skills = [];
2324
        foreach ($result as $item) {
2325
            if (empty($level)) {
2326
                $skills[] = [
2327
                    'name' => self::translateName($item['name']),
2328
                    'acquired_skill_at' => $item['acquired_skill_at'],
2329
                ];
2330
            } else {
2331
                $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

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