Passed
Push — master ( 762454...a92950 )
by Julito
07:28
created

SkillModel::getChildren()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 11
rs 10
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
6
use Chamilo\CoreBundle\Entity\GradebookCategory;
7
use Chamilo\CoreBundle\Entity\Level;
8
use Chamilo\CoreBundle\Entity\Skill;
9
use Chamilo\CoreBundle\Entity\SkillRelCourse;
10
use Chamilo\CoreBundle\Entity\SkillRelGradebook;
11
use Chamilo\CoreBundle\Entity\SkillRelItem;
12
use Chamilo\CoreBundle\Entity\SkillRelItemRelUser;
13
use Chamilo\CoreBundle\Entity\SkillRelSkill;
14
use Chamilo\CoreBundle\Entity\SkillRelUser;
15
use Chamilo\CoreBundle\Entity\User;
16
use Chamilo\CoreBundle\Framework\Container;
17
use Chamilo\CourseBundle\Entity\CAttendance;
18
use Chamilo\CourseBundle\Entity\CForumThread;
19
use Chamilo\CourseBundle\Entity\CLink;
20
use Chamilo\CourseBundle\Entity\CLp;
21
use Chamilo\CourseBundle\Entity\CQuiz;
22
use Chamilo\CourseBundle\Entity\CStudentPublication;
23
use Chamilo\CourseBundle\Entity\CSurvey;
24
use Fhaculty\Graph\Graph;
25
use Fhaculty\Graph\Vertex;
26
27
class SkillModel extends Model
28
{
29
    public $columns = [
30
        'id',
31
        'name',
32
        'description',
33
        'access_url_id',
34
        'updated_at',
35
        'short_code',
36
        'icon',
37
        'criteria',
38
    ];
39
    public array $required = ['name'];
40
41
    /** Array of colours by depth, for the coffee wheel. Each depth has 4 col */
42
    /*var $colours = array(
43
      0 => array('#f9f0ab', '#ecc099', '#e098b0', '#ebe378'),
44
      1 => array('#d5dda1', '#4a5072', '#8dae43', '#72659d'),
45
      2 => array('#b28647', '#2e6093', '#393e64', '#1e8323'),
46
      3 => array('#9f6652', '#9f6652', '#9f6652', '#9f6652'),
47
      4 => array('#af643c', '#af643c', '#af643c', '#af643c'),
48
      5 => array('#72659d', '#72659d', '#72659d', '#72659d'),
49
      6 => array('#8a6e9e', '#8a6e9e', '#8a6e9e', '#8a6e9e'),
50
      7 => array('#92538c', '#92538c', '#92538c', '#92538c'),
51
      8 => array('#2e6093', '#2e6093', '#2e6093', '#2e6093'),
52
      9 => array('#3a5988', '#3a5988', '#3a5988', '#3a5988'),
53
     10 => array('#393e64', '#393e64', '#393e64', '#393e64'),
54
    );*/
55
    public function __construct()
56
    {
57
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL);
58
        $this->table_user = Database::get_main_table(TABLE_MAIN_USER);
59
        $this->table_skill_rel_gradebook = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
60
        $this->table_skill_rel_user = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
61
        $this->table_course = Database::get_main_table(TABLE_MAIN_COURSE);
62
        $this->table_skill_rel_skill = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
63
        $this->table_gradebook = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
64
        $this->sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
65
    }
66
67
    /**
68
     * Gets an element.
69
     *
70
     * @param int $id
71
     *
72
     * @return array|mixed
73
     */
74
    public function get($id)
75
    {
76
        $result = parent::get($id);
77
        if (empty($result)) {
78
            return [];
79
        }
80
81
        // @todo fix badges icons
82
        //$path = api_get_path(WEB_UPLOAD_PATH).'badges/';
83
        $path = '';
84
        /*if (!empty($result['icon'])) {
85
            $iconSmall = sprintf(
86
                '%s-small.png',
87
                sha1($result['name'])
88
            );
89
90
            $iconBig = sprintf(
91
                '%s.png',
92
                sha1($result['name'])
93
            );
94
95
            $iconMini = $path.$iconSmall;
96
            $iconSmall = $path.$iconSmall;
97
            $iconBig = $path.$iconBig;
98
        } else {
99
            $iconMini = Display::returnIconPath('badges-default.png', ICON_SIZE_MEDIUM);
100
            $iconSmall = Display::returnIconPath('badges-default.png', ICON_SIZE_BIG);
101
            $iconBig = Display::returnIconPath('badges-default.png', ICON_SIZE_HUGE);
102
        }*/
103
104
        /*$result['icon_mini'] = $iconMini;
105
        $result['icon_small'] = $iconSmall;
106
        $result['icon_big'] = $iconBig;
107
108
        $result['img_mini'] = Display::img($iconBig, $result['name'], ['width' => ICON_SIZE_MEDIUM]);
109
        $result['img_big'] = Display::img($iconBig, $result['name']);
110
        $result['img_small'] = Display::img($iconSmall, $result['name']);*/
111
        $result['name'] = self::translateName($result['name']);
112
        $result['short_code'] = self::translateCode($result['short_code']);
113
114
        return $result;
115
    }
116
117
    /**
118
     * @param array  $skills
119
     * @param string $imageSize     mini|small|big
120
     * @param bool   $addDivWrapper
121
     *
122
     * @return string
123
     */
124
    public function processSkillList($skills, $imageSize = '', $addDivWrapper = true)
125
    {
126
        if (empty($skills)) {
127
            return '';
128
        }
129
130
        if (empty($imageSize)) {
131
            $imageSize = 'img_small';
132
        } else {
133
            $imageSize = "img_$imageSize";
134
        }
135
136
        $html = '';
137
        if ($addDivWrapper) {
138
            $html = '<div class="scrollbar-inner badges-sidebar">';
139
        }
140
        $html .= '<ul class="list-unstyled list-badges">';
141
        foreach ($skills as $skill) {
142
            if (isset($skill['data'])) {
143
                $skill = $skill['data'];
144
            }
145
            $html .= '<li class="thumbnail">';
146
            $item = $skill[$imageSize];
147
            $item .= '<div class="caption">
148
                        <p class="text-center">'.$skill['name'].'</p>
149
                      </div>';
150
            if (isset($skill['url'])) {
151
                $html .= Display::url($item, $skill['url'], ['target' => '_blank']);
152
            } else {
153
                $html .= $item;
154
            }
155
            $html .= '</li>';
156
        }
157
        $html .= '</ul>';
158
159
        if ($addDivWrapper) {
160
            $html .= '</div>';
161
        }
162
163
        return $html;
164
    }
165
166
    /**
167
     * @param $skills
168
     * @param string $imageSize mini|small|big
169
     * @param string $style
170
     * @param bool   $showBadge
171
     * @param bool   $showTitle
172
     *
173
     * @return string
174
     */
175
    public function processSkillListSimple($skills, $imageSize = 'mini', $style = '', $showBadge = true, $showTitle = true)
176
    {
177
        if (empty($skills)) {
178
            return '';
179
        }
180
181
        $imageParams = '';
182
        switch ($imageSize) {
183
            case 'mini':
184
                $imageParams = '?w='.ICON_SIZE_MEDIUM;
185
                break;
186
            case 'small':
187
                $imageParams = '?w='.ICON_SIZE_BIG;
188
                break;
189
            case 'big':
190
                $imageParams = '?w='.ICON_SIZE_HUGE;
191
                break;
192
        }
193
194
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
195
        $skillRepo = Container::getSkillRepository();
196
        $html = '';
197
        foreach ($skills as $skill) {
198
            if (isset($skill['data'])) {
199
                $skill = $skill['data'];
200
            }
201
202
            $item = '';
203
            if ($showBadge) {
204
                $skillEntity = $skillRepo->find($skill['id']);
205
                $url = $this->getWebIconPath($skillEntity);
206
207
                $item = '<div class="item"><img src="'.$url.$imageParams.'" /></div>';
208
            }
209
210
            $name = '<div class="caption">'.$skill['name'].'</div>';
211
            if (!empty($skill['short_code'])) {
212
                $name = $skill['short_code'];
213
            }
214
215
            if (!$isHierarchicalTable) {
216
                //$item .= '<br />';
217
            }
218
219
            if ($showTitle) {
220
                $item .= $name;
221
            }
222
223
            if (isset($skill['url'])) {
224
                $html .= Display::url($item, $skill['url'], ['target' => '_blank', 'style' => $style]);
225
            } else {
226
                $html .= Display::url($item, '#', ['target' => '_blank', 'style' => $style]);
227
            }
228
        }
229
230
        return $html;
231
    }
232
233
    /**
234
     * @param int $id
235
     *
236
     * @return array
237
     */
238
    public function getSkillInfo($id)
239
    {
240
        $skillRelSkill = new SkillRelSkillModel();
241
        $skillInfo = $this->get($id);
242
        if (!empty($skillInfo)) {
243
            $skillInfo['extra'] = $skillRelSkill->getSkillInfo($id);
244
            $skillInfo['gradebooks'] = $this->getGradebooksBySkill($id);
245
        }
246
247
        return $skillInfo;
248
    }
249
250
    /**
251
     * @param array $skill_list
252
     *
253
     * @return array
254
     */
255
    public function getSkillsInfo($skill_list)
256
    {
257
        $skill_list = array_map('intval', $skill_list);
258
        $skill_list = implode("', '", $skill_list);
259
260
        $sql = "SELECT * FROM {$this->table}
261
                WHERE id IN ('$skill_list') ";
262
263
        $result = Database::query($sql);
264
        $skills = Database::store_result($result, 'ASSOC');
265
266
        foreach ($skills as &$skill) {
267
            if (!$skill['icon']) {
268
                continue;
269
            }
270
271
            $skill['icon_small'] = sprintf(
272
                'badges/%s-small.png',
273
                sha1($skill['name'])
274
            );
275
            $skill['name'] = self::translateName($skill['name']);
276
            $skill['short_code'] = self::translateCode($skill['short_code']);
277
        }
278
279
        return $skills;
280
    }
281
282
    /**
283
     * @param bool $load_user_data
284
     * @param bool $user_id
285
     * @param int  $id
286
     * @param int  $parent_id
287
     *
288
     * @return array
289
     */
290
    public function getAllSkills(
291
        $load_user_data = false,
292
        $user_id = false,
293
        $id = null,
294
        $parent_id = null
295
    ) {
296
        $id_condition = '';
297
        if (!empty($id)) {
298
            $id = (int) $id;
299
            $id_condition = " WHERE s.id = $id";
300
        }
301
302
        if (!empty($parent_id)) {
303
            $parent_id = (int) $parent_id;
304
            if (empty($id_condition)) {
305
                $id_condition = " WHERE ss.parent_id = $parent_id";
306
            } else {
307
                $id_condition = " AND ss.parent_id = $parent_id";
308
            }
309
        }
310
311
        $skillRepo = Container::getSkillRepository();
312
        $assetRepo = Container::getAssetRepository();
313
314
        $sql = "SELECT
315
                    s.id,
316
                    s.name,
317
                    s.description,
318
                    ss.parent_id,
319
                    ss.relation_type,
320
                    s.icon,
321
                    s.short_code,
322
                    s.status
323
                FROM {$this->table} s
324
                INNER JOIN {$this->table_skill_rel_skill} ss
325
                ON (s.id = ss.skill_id) $id_condition
326
                ORDER BY ss.id, ss.parent_id";
327
328
        $result = Database::query($sql);
329
        $skills = [];
330
        if (Database::num_rows($result)) {
331
            while ($row = Database::fetch_array($result, 'ASSOC')) {
332
                $skillId = $row['id'];
333
                $skill = $skillRepo->find($skillId);
334
335
                $row['asset'] = '';
336
                if ($skill->getAsset()) {
337
                    $row['asset'] = $assetRepo->getAssetUrl($skill->getAsset());
338
                }
339
340
                $row['name'] = self::translateName($skill->getName());
341
                $row['short_code'] = self::translateCode($skill->getShortCode());
342
                $skillRelSkill = new SkillRelSkillModel();
343
                $parents = $skillRelSkill->getSkillParents($skillId);
344
                $row['level'] = count($parents) - 1;
345
                $row['gradebooks'] = $this->getGradebooksBySkill($skillId);
346
                $skills[$row['id']] = $row;
347
            }
348
        }
349
350
        // Load all children of the parent_id
351
        if (!empty($skills) && !empty($parent_id)) {
352
            foreach ($skills as $skill) {
353
                $children = self::get_all($load_user_data, $user_id, $id, $skill['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method Model::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

353
                /** @scrutinizer ignore-call */ 
354
                $children = self::get_all($load_user_data, $user_id, $id, $skill['id']);
Loading history...
354
                if (!empty($children)) {
355
                    //$skills = array_merge($skills, $children);
356
                    $skills = $skills + $children;
357
                }
358
            }
359
        }
360
361
        return $skills;
362
    }
363
364
    /**
365
     * @param int $skill_id
366
     *
367
     * @return array|resource
368
     */
369
    public function getGradebooksBySkill($skill_id)
370
    {
371
        $skill_id = (int) $skill_id;
372
        $sql = "SELECT g.* FROM {$this->table_gradebook} g
373
                INNER JOIN {$this->table_skill_rel_gradebook} sg
374
                ON g.id = sg.gradebook_id
375
                WHERE sg.skill_id = $skill_id";
376
        $result = Database::query($sql);
377
        $result = Database::store_result($result, 'ASSOC');
378
379
        return $result;
380
    }
381
382
    /**
383
     * Get one level children.
384
     *
385
     * @param int  $skill_id
386
     * @param bool $load_user_data
387
     *
388
     * @return array
389
     */
390
    public function getChildren($skill_id, $load_user_data = false)
391
    {
392
        $skillRelSkill = new SkillRelSkillModel();
393
        if ($load_user_data) {
394
            $user_id = api_get_user_id();
395
            $skills = $skillRelSkill->getChildren($skill_id, true, $user_id);
396
        } else {
397
            $skills = $skillRelSkill->getChildren($skill_id);
398
        }
399
400
        return $skills;
401
    }
402
403
    /**
404
     * Get all children of the current node (recursive).
405
     *
406
     * @param int $skillId
407
     *
408
     * @return array
409
     */
410
    public function getAllChildren($skillId)
411
    {
412
        $skillRelSkill = new SkillRelSkillModel();
413
        $children = $skillRelSkill->getChildren($skillId);
414
        foreach ($children as $child) {
415
            $subChildren = $this->getAllChildren($child['id']);
416
        }
417
418
        if (!empty($subChildren)) {
419
            $children = array_merge($children, $subChildren);
420
        }
421
422
        return $children;
423
    }
424
425
    /**
426
     * Gets all parents from from the wanted skill.
427
     */
428
    public function get_parents($skillId)
429
    {
430
        $skillRelSkill = new SkillRelSkillModel();
431
        $skills = $skillRelSkill->getSkillParents($skillId, true);
432
        foreach ($skills as &$skill) {
433
            $skill['data'] = $this->get($skill['skill_id']);
434
        }
435
436
        return $skills;
437
    }
438
439
    /**
440
     * All direct parents.
441
     *
442
     * @param int $skillId
443
     *
444
     * @return array
445
     */
446
    public function getDirectParents($skillId)
447
    {
448
        $skillRelSkill = new SkillRelSkillModel();
449
        $skills = $skillRelSkill->getDirectParents($skillId, true);
450
        if (!empty($skills)) {
451
            foreach ($skills as &$skill) {
452
                $skillData = $this->get($skill['skill_id']);
453
                if (empty($skillData)) {
454
                    continue;
455
                }
456
                $skill['data'] = $skillData;
457
                $skill_info2 = $skillRelSkill->getSkillInfo($skill['skill_id']);
458
                $parentId = isset($skill_info2['parent_id']) ? isset($skill_info2['parent_id']) : 0;
459
                $skill['data']['parent_id'] = $parentId;
460
            }
461
462
            return $skills;
463
        }
464
465
        return [];
466
    }
467
468
    /**
469
     * Adds a new skill.
470
     *
471
     * @param array $params
472
     *
473
     * @return bool|null
474
     */
475
    public function add(array $params)
476
    {
477
        if (empty($params['parent_id'])) {
478
            $params['parent_id'] = 1;
479
        }
480
481
        if (!is_array($params['parent_id'])) {
482
            $params['parent_id'] = [$params['parent_id']];
483
        }
484
485
        $skillRelSkill = new SkillRelSkillModel();
486
487
        // Saving name, description
488
        $params['access_url_id'] = api_get_current_access_url_id();
489
        $params['icon'] = '';
490
491
        $skill_id = $this->save($params);
492
        $em = Database::getManager();
493
        $repo = $em->getRepository(Skill::class);
494
        $repoGradebook = $em->getRepository(GradebookCategory::class);
495
496
        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...
497
            $skill = $repo->find($skill_id);
498
            // Saving skill_rel_skill (parent_id, relation_type)
499
            foreach ($params['parent_id'] as $parent_id) {
500
                $relation_exists = $skillRelSkill->relationExists($skill_id, $parent_id);
501
                if (!$relation_exists) {
502
                    $skillRelSkill =
503
                        (new SkillRelSkill())
504
                            ->setSkill($skill)
505
                            ->setParent($repo->find($parent_id))
506
                            ->setLevel($params['level'] ?? 0)
507
                            ->setRelationType($params['relation_type'] ?? 0)
508
                    ;
509
                    $em->persist($skillRelSkill);
510
                    $em->flush();
511
                }
512
            }
513
514
            if (!empty($params['gradebook_id'])) {
515
                foreach ($params['gradebook_id'] as $gradebook_id) {
516
                    $skillRelGradebook = (new SkillRelGradebook())
517
                        ->setGradeBookCategory($repoGradebook->find($gradebook_id))
518
                        ->setSkill($skill)
519
                    ;
520
                    $em->persist($skillRelGradebook);
521
                    $em->flush();
522
                }
523
            }
524
525
            return $skill_id;
526
        }
527
528
        return null;
529
    }
530
531
    /**
532
     * @param int $userId
533
     * @param int $courseId
534
     * @param int $sessionId
535
     *
536
     * @return bool
537
     */
538
    public function addSkillToUser(
539
        $userId,
540
        GradebookCategory $category,
541
        $courseId,
542
        $sessionId
543
    ) {
544
        $skill_rel_user = new SkillRelUserModel();
545
546
        // Load subcategories
547
        if ($category->hasSubCategories()) {
548
            $subCategories = $category->getSubCategories();
549
            if (!empty($subCategories)) {
550
                foreach ($subCategories as $subCategory) {
551
                    $this->addSkillToUser($userId, $subCategory, $courseId, $sessionId);
552
                }
553
            }
554
        }
555
556
        $skills = $category->getSkills();
557
        if (!empty($skills)) {
558
            foreach ($skills as $skill) {
559
                $skillId = $skill->getSkill()->getId();
560
                $hasSkill = $this->userHasSkill(
561
                    $userId,
562
                    $skillId,
563
                    $courseId,
564
                    $sessionId
565
                );
566
567
                if (!$hasSkill) {
568
                    $params = [
569
                        'user_id' => $userId,
570
                        'skill_id' => $skillId,
571
                        'acquired_skill_at' => api_get_utc_datetime(),
572
                        'course_id' => (int) $courseId,
573
                        'session_id' => $sessionId ? (int) $sessionId : null,
574
                    ];
575
                    $skill_rel_user->save($params);
576
                }
577
            }
578
        }
579
580
        return true;
581
    }
582
583
    /* Deletes a skill */
584
    public function delete($id)
585
    {
586
        /*$params = array('skill_id' => $skill_id);
587
588
        $skillRelSkill     = new SkillRelSkill();
589
        $skills = $skillRelSkill->get_all(array('where'=>array('skill_id = ?' =>$skill_id)));
590
591
        $skill_rel_profile     = new SkillRelProfile();
592
        $skillRelGradebook = new SkillRelGradebook();
593
        $skill_rel_user     = new SkillRelUser();
594
595
        $this->delete($skill_id);
596
597
        $skillRelGradebook->delete($params);*/
598
    }
599
600
    public function edit(array $params)
601
    {
602
        if (empty($params['parent_id'])) {
603
            $params['parent_id'] = 1;
604
        }
605
606
        $params['gradebook_id'] = $params['gradebook_id'] ?? [];
607
608
        $skillRelSkill = new SkillRelSkillModel();
609
        $skillRelGradebook = new SkillRelGradebookModel();
610
611
        // Saving name, description
612
        $this->update($params);
613
        $skillId = $params['id'];
614
615
        if ($skillId) {
616
            // Saving skill_rel_skill (parent_id, relation_type)
617
            if (!is_array($params['parent_id'])) {
618
                $params['parent_id'] = [$params['parent_id']];
619
            }
620
621
            // Cannot change parent of root
622
            if (1 == $skillId) {
623
                $params['parent_id'] = 0;
624
            }
625
626
            foreach ($params['parent_id'] as $parent_id) {
627
                $relation_exists = $skillRelSkill->relationExists($skillId, $parent_id);
628
                if (!$relation_exists) {
629
                    $attributes = [
630
                        'skill_id' => $skillId,
631
                        'parent_id' => $parent_id,
632
                        'relation_type' => $params['relation_type'] ?? 0,
633
                        //'level'         => $params['level'],
634
                    ];
635
                    $skillRelSkill->updateBySkill($attributes);
636
                }
637
            }
638
639
            $skillRelGradebook->updateGradeBookListBySkill($skillId, $params['gradebook_id']);
640
641
            return $skillId;
642
        }
643
644
        return null;
645
    }
646
647
    /**
648
     * Get user's skills.
649
     *
650
     * @param int  $userId
651
     * @param bool $getSkillData
652
     * @param int  $courseId
653
     * @param int  $sessionId
654
     *
655
     * @return array
656
     */
657
    public function getUserSkills($userId, $getSkillData = false, $courseId = 0, $sessionId = 0)
658
    {
659
        $userId = (int) $userId;
660
        $courseId = (int) $courseId;
661
        $sessionId = (int) $sessionId;
662
663
        $courseCondition = '';
664
        if (!empty($courseId)) {
665
            $courseCondition = " AND course_id = $courseId ";
666
        }
667
668
        $sessionCondition = '';
669
        if (!empty($sessionId)) {
670
            $sessionCondition = " AND course_id = $sessionId ";
671
        }
672
673
        $sql = 'SELECT DISTINCT
674
                    s.id,
675
                    s.name,
676
                    s.icon,
677
                    s.asset_id,
678
                    u.id as issue,
679
                    u.acquired_skill_at,
680
                    u.course_id
681
                FROM '.$this->table_skill_rel_user.' u
682
                INNER JOIN '.$this->table.' s
683
                ON u.skill_id = s.id
684
                WHERE
685
                    user_id = '.$userId.' '.$sessionCondition.' '.$courseCondition;
686
687
        $result = Database::query($sql);
688
        $skills = Database::store_result($result, 'ASSOC');
689
        $skillList = [];
690
        if (!empty($skills)) {
691
            foreach ($skills as $skill) {
692
                if ($getSkillData) {
693
                    $skillData = $this->get($skill['id']);
694
                    $skillData['url'] = api_get_path(WEB_PATH).'badge/'.$skill['id'].'/user/'.$userId;
695
                    $skillList[$skill['id']] = array_merge($skill, $skillData);
696
                } else {
697
                    $skillList[$skill['id']] = $skill['id'];
698
                }
699
            }
700
        }
701
702
        return $skillList;
703
    }
704
705
    /**
706
     * @param array $skills
707
     * @param int   $level
708
     *
709
     * @return string
710
     */
711
    public function processVertex(Vertex $vertex, $skills = [], $level = 0)
712
    {
713
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
714
        $subTable = '';
715
        if ($vertex->getVerticesEdgeTo()->count() > 0) {
716
            if ($isHierarchicalTable) {
717
                $subTable .= '<ul>';
718
            }
719
            foreach ($vertex->getVerticesEdgeTo() as $subVertex) {
720
                $data = $subVertex->getAttribute('graphviz.data');
721
                $passed = in_array($data['id'], array_keys($skills));
722
                $transparency = '';
723
                if (false === $passed) {
724
                    // @todo use css class
725
                    $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
726
                }
727
728
                if ($isHierarchicalTable) {
729
                    $label = $this->processSkillListSimple([$data], 'mini', $transparency);
730
                    $subTable .= '<li>'.$label;
731
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
732
                    $subTable .= '</li>';
733
                } else {
734
                    $imageSize = 'mini';
735
                    if (2 == $level) {
736
                        $imageSize = 'small';
737
                    }
738
                    $showTitle = true;
739
                    if ($level > 2) {
740
                        $showTitle = false;
741
                    }
742
743
                    $label = $this->processSkillListSimple([$data], $imageSize, $transparency, true, $showTitle);
744
                    $subTable .= '<div class="thumbnail" style="float:left; margin-right:5px; ">';
745
                    $subTable .= '<div style="'.$transparency.'">';
746
747
                    $subTable .= '<div style="text-align: center">';
748
                    $subTable .= $label;
749
                    $subTable .= '</div>';
750
751
                    $subTable .= '</div>';
752
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
753
                    $subTable .= '</div>';
754
                }
755
            }
756
757
            if ($isHierarchicalTable) {
758
                $subTable .= '</ul>';
759
            }
760
        }
761
762
        return $subTable;
763
    }
764
765
    /**
766
     * @param int  $userId
767
     * @param int  $courseId
768
     * @param int  $sessionId
769
     * @param bool $addTitle
770
     *
771
     * @return array
772
     */
773
    public function getUserSkillsTable($userId, $courseId = 0, $sessionId = 0, $addTitle = true)
774
    {
775
        $skills = $this->getUserSkills($userId, true, $courseId, $sessionId);
776
        $courseTempList = [];
777
        $tableRows = [];
778
        $skillParents = [];
779
        foreach ($skills as $resultData) {
780
            $parents = $this->get_parents($resultData['id']);
781
            foreach ($parents as $parentData) {
782
                $parentData['passed'] = in_array($parentData['id'], array_keys($skills));
783
                if ($parentData['passed'] && isset($skills[$parentData['id']]['url'])) {
784
                    $parentData['data']['url'] = $skills[$parentData['id']]['url'];
785
                }
786
                $skillParents[$resultData['id']][$parentData['id']] = $parentData;
787
            }
788
        }
789
790
        foreach ($skills as $resultData) {
791
            $courseId = $resultData['course_id'];
792
            if (!empty($courseId)) {
793
                if (isset($courseTempList[$courseId])) {
794
                    $courseInfo = $courseTempList[$courseId];
795
                } else {
796
                    $courseInfo = api_get_course_info_by_id($courseId);
797
                    $courseTempList[$courseId] = $courseInfo;
798
                }
799
            } else {
800
                $courseInfo = [];
801
            }
802
            $tableRow = [
803
                'skill_id' => $resultData['id'],
804
                'asset_id' => $resultData['asset_id'],
805
                'skill_name' => self::translateName($resultData['name']),
806
                'short_code' => $resultData['short_code'],
807
                'skill_url' => $resultData['url'],
808
                'achieved_at' => api_get_local_time($resultData['acquired_skill_at']),
809
                'course_image' => '',
810
                'course_name' => '',
811
            ];
812
813
            if (!empty($courseInfo)) {
814
                $tableRow['course_image'] = $courseInfo['course_image'];
815
                $tableRow['course_name'] = $courseInfo['title'];
816
            }
817
            $tableRows[] = $tableRow;
818
        }
819
820
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
821
        $allowLevels = api_get_configuration_value('skill_levels_names');
822
823
        $tableResult = '<div id="skillList">';
824
        if ($isHierarchicalTable) {
825
            $tableResult = '<div class="table-responsive">';
826
        }
827
828
        if ($addTitle) {
829
            $tableResult .= Display::page_subheader(get_lang('Achieved skills'));
830
            $tableResult .= '<div class="skills-badges">';
831
        }
832
833
        if (!empty($skillParents)) {
834
            if (empty($allowLevels)) {
835
                $tableResult .= $this->processSkillListSimple($skills);
836
            } else {
837
                $graph = new Graph();
838
                $graph->setAttribute('graphviz.graph.rankdir', 'LR');
839
                foreach ($skillParents as $parentList) {
840
                    $old = null;
841
                    foreach ($parentList as $parent) {
842
                        if ($graph->hasVertex($parent['id'])) {
843
                            $current = $graph->getVertex($parent['id']);
844
                        } else {
845
                            $current = $graph->createVertex($parent['id']);
846
                            $current->setAttribute('graphviz.data', $parent['data']);
847
                        }
848
849
                        if (!empty($old)) {
850
                            if ($graph->hasVertex($old['id'])) {
851
                                $nextVertex = $graph->getVertex($old['id']);
852
                            } else {
853
                                $nextVertex = $graph->createVertex($old['id']);
854
                                $nextVertex->setAttribute('graphviz.data', $old['data']);
855
                            }
856
857
                            if (!$nextVertex->hasEdgeTo($current)) {
858
                                $nextVertex->createEdgeTo($current);
859
                            }
860
                        }
861
                        $old = $parent;
862
                    }
863
                }
864
865
                if ($isHierarchicalTable) {
866
                    $table = '<table class ="table table-bordered">';
867
                    // Getting "root" vertex
868
                    $root = $graph->getVertex(1);
869
                    $table .= '<tr>';
870
                    /** @var Vertex $vertex */
871
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
872
                        $data = $vertex->getAttribute('graphviz.data');
873
874
                        $passed = in_array($data['id'], array_keys($skills));
875
                        $transparency = '';
876
                        if (false === $passed) {
877
                            // @todo use a css class
878
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
879
                        }
880
881
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency);
882
                        $table .= '<td >';
883
884
                        $table .= '<div class="skills_chart"> <ul><li>'.$label;
885
                        $table .= $this->processVertex($vertex, $skills);
886
                        $table .= '</ul></li></div>';
887
                        $table .= '</td>';
888
                    }
889
                    $table .= '</tr></table>';
890
                } else {
891
                    // Getting "root" vertex
892
                    $root = $graph->getVertex(1);
893
                    $table = '';
894
                    /** @var Vertex $vertex */
895
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
896
                        $data = $vertex->getAttribute('graphviz.data');
897
898
                        $passed = in_array($data['id'], array_keys($skills));
899
                        $transparency = '';
900
                        if (false === $passed) {
901
                            // @todo use a css class
902
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
903
                        }
904
905
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency, false);
906
907
                        $skillTable = $this->processVertex($vertex, $skills, 2);
908
                        $table .= "<h3>$label</h3>";
909
910
                        if (!empty($skillTable)) {
911
                            $table .= '<table class ="table table-bordered">';
912
                            $table .= '<tr>';
913
                            $table .= '<td>';
914
                            $table .= '<div>';
915
                            $table .= $skillTable;
916
                            $table .= '</div>';
917
                            $table .= '</td>';
918
                            $table .= '</tr></table>';
919
                        }
920
                    }
921
                }
922
923
                $tableResult .= $table;
924
            }
925
        } else {
926
            $tableResult .= get_lang('Without achieved skills');
927
        }
928
929
        if ($addTitle) {
930
            $tableResult .= '</div>';
931
        }
932
        $tableResult .= '</div>';
933
934
        return [
935
            'skills' => $tableRows,
936
            'table' => $tableResult,
937
        ];
938
    }
939
940
    /**
941
     * @param int  $user_id
942
     * @param int  $skill_id
943
     * @param bool $return_flat_array
944
     * @param bool $add_root
945
     *
946
     * @return array|null
947
     */
948
    public function getSkillsTree(
949
        $user_id = null,
950
        $skill_id = null,
951
        $return_flat_array = false,
952
        $add_root = false
953
    ) {
954
        if (1 == $skill_id) {
955
            $skill_id = 0;
956
        }
957
        if (isset($user_id) && !empty($user_id)) {
958
            $skills = $this->getAllSkills(true, $user_id, null, $skill_id);
959
        } else {
960
            $skills = $this->getAllSkills(false, false, null, $skill_id);
961
        }
962
963
        $original_skill = $this->list = $skills;
964
965
        // Show 1 item
966
        if (!empty($skill_id)) {
967
            if ($add_root) {
968
                if (!empty($skill_id)) {
969
                    // Default root node
970
                    $skills[1] = [
971
                        'id' => '1',
972
                        'name' => get_lang('Root'),
973
                        'parent_id' => 0,
974
                        'status' => 1,
975
                    ];
976
                    $skillInfo = $this->getSkillInfo($skill_id);
977
978
                    // 2nd node
979
                    $skills[$skill_id] = $skillInfo;
980
                    // Uncomment code below to hide the searched skill
981
                    $skills[$skill_id]['data']['parent_id'] = $skillInfo['extra']['parent_id'];
982
                    $skills[$skill_id]['parent_id'] = 1;
983
                }
984
            }
985
        }
986
987
        $refs = [];
988
        $skills_tree = null;
989
990
        // Create references for all nodes
991
        $flat_array = [];
992
        $family = [];
993
        if (!empty($skills)) {
994
            foreach ($skills as &$skill) {
995
                if (0 == $skill['parent_id']) {
996
                    $skill['parent_id'] = 1;
997
                }
998
999
                // because except main keys (id, name, children) others keys
1000
                // are not saved while in the space tree
1001
                $skill['data'] = ['parent_id' => $skill['parent_id']];
1002
1003
                // If a short code was defined, send the short code to replace
1004
                // skill name (to shorten the text in the wheel)
1005
                if (!empty($skill['short_code']) &&
1006
                    'false' === api_get_setting('show_full_skill_name_on_skill_wheel')
1007
                ) {
1008
                    $skill['data']['short_code'] = $skill['short_code'];
1009
                }
1010
1011
                $skill['data']['name'] = $skill['name'];
1012
                $skill['data']['status'] = $skill['status'];
1013
1014
                // In order to paint all members of a family with the same color
1015
                if (empty($skill_id)) {
1016
                    if (1 == $skill['parent_id']) {
1017
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1018
                    }
1019
                } else {
1020
                    if ($skill['parent_id'] == $skill_id) {
1021
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1022
                    }
1023
                    /*if ($skill_id == $skill['id']) {
1024
                        $skill['parent_id'] = 1;
1025
                    }*/
1026
                }
1027
1028
                if (!isset($skill['data']['real_parent_id'])) {
1029
                    $skill['data']['real_parent_id'] = $skill['parent_id'];
1030
                }
1031
1032
                // User achieved the skill (depends in the gradebook with certification)
1033
                $skill['data']['achieved'] = false;
1034
                if ($user_id) {
1035
                    $skill['data']['achieved'] = $this->userHasSkill(
1036
                        $user_id,
1037
                        $skill['id']
1038
                    );
1039
                }
1040
1041
                // Check if the skill has related gradebooks
1042
                $skill['data']['skill_has_gradebook'] = false;
1043
                if (isset($skill['gradebooks']) && !empty($skill['gradebooks'])) {
1044
                    $skill['data']['skill_has_gradebook'] = true;
1045
                }
1046
                $refs[$skill['id']] = &$skill;
1047
                $flat_array[$skill['id']] = &$skill;
1048
            }
1049
1050
            // Checking family value
1051
1052
            $family_id = 1;
1053
            $new_family_array = [];
1054
            foreach ($family as $main_family_id => $family_items) {
1055
                if (!empty($family_items)) {
1056
                    foreach ($family_items as $item) {
1057
                        $new_family_array[$item['id']] = $family_id;
1058
                    }
1059
                }
1060
                $new_family_array[$main_family_id] = $family_id;
1061
                $family_id++;
1062
            }
1063
1064
            if (empty($original_skill)) {
1065
                $refs[1]['children'][0] = $skills[1];
1066
                $skills[$skill_id]['data']['family_id'] = 1;
1067
                $refs[1]['children'][0]['children'][0] = $skills[$skill_id];
1068
                $flat_array[$skill_id] = $skills[$skill_id];
1069
            } else {
1070
                // Moving node to the children index of their parents
1071
                foreach ($skills as $my_skill_id => &$skill) {
1072
                    if (isset($new_family_array[$skill['id']])) {
1073
                        $skill['data']['family_id'] = $new_family_array[$skill['id']];
1074
                    }
1075
                    $refs[$skill['parent_id']]['children'][] = &$skill;
1076
                    $flat_array[$my_skill_id] = $skill;
1077
                }
1078
            }
1079
1080
            $skills_tree = [
1081
                'name' => get_lang('Absolute skill'),
1082
                'id' => 1,
1083
                'children' => $refs[1]['children'],
1084
                'data' => [],
1085
            ];
1086
        }
1087
1088
        if ($return_flat_array) {
1089
            return $flat_array;
1090
        }
1091
        unset($skills);
1092
1093
        return $skills_tree;
1094
    }
1095
1096
    /**
1097
     * Get skills tree as a simplified JSON structure.
1098
     *
1099
     * @param int user id
1100
     * @param int skill id
1101
     * @param bool return a flat array or not
1102
     * @param int depth of the skills
1103
     *
1104
     * @return string json
1105
     */
1106
    public function getSkillsTreeToJson(
1107
        $user_id = null,
1108
        $skill_id = null,
1109
        $return_flat_array = false,
1110
        $main_depth = 2
1111
    ) {
1112
        $tree = $this->getSkillsTree(
1113
            $user_id,
1114
            $skill_id,
1115
            $return_flat_array,
1116
            true
1117
        );
1118
        $simple_tree = [];
1119
        if (!empty($tree['children'])) {
1120
            foreach ($tree['children'] as $element) {
1121
                $children = [];
1122
                if (isset($element['children'])) {
1123
                    $children = $this->getSkillToJson($element['children'], 1, $main_depth);
1124
                }
1125
                $simple_tree[] = [
1126
                    'name' => $element['name'],
1127
                    'children' => $children,
1128
                ];
1129
            }
1130
        }
1131
1132
        return json_encode($simple_tree);
1133
    }
1134
1135
    /**
1136
     * Get JSON element.
1137
     *
1138
     * @param array $subtree
1139
     * @param int   $depth
1140
     * @param int   $max_depth
1141
     *
1142
     * @return array|null
1143
     */
1144
    public function getSkillToJson($subtree, $depth = 1, $max_depth = 2)
1145
    {
1146
        $simple_sub_tree = [];
1147
        if (is_array($subtree)) {
1148
            $counter = 1;
1149
            foreach ($subtree as $elem) {
1150
                $tmp = [];
1151
                $tmp['name'] = $elem['name'];
1152
                $tmp['id'] = $elem['id'];
1153
                $tmp['isSearched'] = self::isSearched($elem['id']);
1154
1155
                if (isset($elem['children']) && is_array($elem['children'])) {
1156
                    $tmp['children'] = $this->getSkillToJson(
1157
                        $elem['children'],
1158
                        $depth + 1,
1159
                        $max_depth
1160
                    );
1161
                }
1162
1163
                if ($depth > $max_depth) {
1164
                    continue;
1165
                }
1166
1167
                $tmp['depth'] = $depth;
1168
                $tmp['counter'] = $counter;
1169
                $counter++;
1170
1171
                if (isset($elem['data']) && is_array($elem['data'])) {
1172
                    foreach ($elem['data'] as $key => $item) {
1173
                        $tmp[$key] = $item;
1174
                    }
1175
                }
1176
                $simple_sub_tree[] = $tmp;
1177
            }
1178
1179
            return $simple_sub_tree;
1180
        }
1181
1182
        return null;
1183
    }
1184
1185
    /**
1186
     * @param int $user_id
1187
     *
1188
     * @return bool
1189
     */
1190
    public function getUserSkillRanking($user_id)
1191
    {
1192
        $user_id = (int) $user_id;
1193
        $sql = "SELECT count(skill_id) count
1194
                FROM {$this->table} s
1195
                INNER JOIN {$this->table_skill_rel_user} su
1196
                ON (s.id = su.skill_id)
1197
                WHERE user_id = $user_id";
1198
        $result = Database::query($sql);
1199
        if (Database::num_rows($result)) {
1200
            $result = Database::fetch_row($result);
1201
1202
            return $result[0];
1203
        }
1204
1205
        return false;
1206
    }
1207
1208
    /**
1209
     * @param $start
1210
     * @param $limit
1211
     * @param $sidx
1212
     * @param $sord
1213
     * @param $where_condition
1214
     *
1215
     * @return array
1216
     */
1217
    public function getUserListSkillRanking(
1218
        $start,
1219
        $limit,
1220
        $sidx,
1221
        $sord,
1222
        $where_condition
1223
    ) {
1224
        $start = (int) $start;
1225
        $limit = (int) $limit;
1226
1227
        /*  ORDER BY $sidx $sord */
1228
        $sql = "SELECT *, @rownum:=@rownum+1 rank FROM (
1229
                    SELECT u.id as user_id, firstname, lastname, count(username) skills_acquired
1230
                    FROM {$this->table} s INNER JOIN {$this->table_skill_rel_user} su
1231
                    ON (s.id = su.skill_id)
1232
                    INNER JOIN {$this->table_user} u
1233
                    ON u.id = su.user_id, (SELECT @rownum:=0) r
1234
                    WHERE 1=1 $where_condition
1235
                    GROUP BY username
1236
                    ORDER BY skills_acquired desc
1237
                    LIMIT $start , $limit)  AS T1, (SELECT @rownum:=0) r";
1238
        $result = Database::query($sql);
1239
        if (Database::num_rows($result)) {
1240
            return Database::store_result($result, 'ASSOC');
1241
        }
1242
1243
        return [];
1244
    }
1245
1246
    /**
1247
     * @return int
1248
     */
1249
    public function getUserListSkillRankingCount()
1250
    {
1251
        $sql = "SELECT count(*) FROM (
1252
                    SELECT count(distinct 1)
1253
                    FROM {$this->table} s
1254
                    INNER JOIN {$this->table_skill_rel_user} su
1255
                    ON (s.id = su.skill_id)
1256
                    INNER JOIN {$this->table_user} u
1257
                    ON u.id = su.user_id
1258
                    GROUP BY username
1259
                 ) as T1";
1260
        $result = Database::query($sql);
1261
        if (Database::num_rows($result)) {
1262
            $result = Database::fetch_row($result);
1263
1264
            return $result[0];
1265
        }
1266
1267
        return 0;
1268
    }
1269
1270
    /**
1271
     * @param string $courseCode
1272
     *
1273
     * @return int
1274
     */
1275
    public function getCountSkillsByCourse($courseCode)
1276
    {
1277
        $courseCode = Database::escape_string($courseCode);
1278
        $sql = "SELECT count(skill_id) as count
1279
                FROM {$this->table_gradebook} g
1280
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1281
                ON g.id = sg.gradebook_id
1282
                WHERE course_code = '$courseCode'";
1283
1284
        $result = Database::query($sql);
1285
        if (Database::num_rows($result)) {
1286
            $result = Database::fetch_row($result);
1287
1288
            return $result[0];
1289
        }
1290
1291
        return 0;
1292
    }
1293
1294
    /**
1295
     * @param int $skillId
1296
     *
1297
     * @return array
1298
     */
1299
    public function getCoursesBySkill($skillId)
1300
    {
1301
        $skillId = (int) $skillId;
1302
        $sql = "SELECT c.title, c.code
1303
                FROM {$this->table_gradebook} g
1304
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1305
                ON g.id = sg.gradebook_id
1306
                INNER JOIN {$this->table_course} c
1307
                ON c.code = g.course_code
1308
                WHERE sg.skill_id = $skillId
1309
                AND (g.session_id IS NULL OR g.session_id = 0)";
1310
        $result = Database::query($sql);
1311
1312
        return Database::store_result($result, 'ASSOC');
1313
    }
1314
1315
    /**
1316
     * Check if the user has the skill.
1317
     *
1318
     * @param int $userId    The user id
1319
     * @param int $skillId   The skill id
1320
     * @param int $courseId  Optional. The course id
1321
     * @param int $sessionId Optional. The session id
1322
     *
1323
     * @return bool Whether the user has the skill return true. Otherwise return false
1324
     */
1325
    public function userHasSkill($userId, $skillId, $courseId = 0, $sessionId = 0)
1326
    {
1327
        $courseId = (int) $courseId;
1328
        $sessionId = (int) $sessionId;
1329
1330
        $whereConditions = [
1331
            'user_id = ? ' => (int) $userId,
1332
            'AND skill_id = ? ' => (int) $skillId,
1333
        ];
1334
1335
        if ($courseId > 0) {
1336
            $whereConditions['AND course_id = ? '] = $courseId;
1337
            $whereConditions['AND session_id = ? '] = $sessionId ? $sessionId : null;
1338
        }
1339
1340
        $result = Database::select(
1341
            'COUNT(1) AS qty',
1342
            $this->table_skill_rel_user,
1343
            [
1344
                'where' => $whereConditions,
1345
            ],
1346
            'first'
1347
        );
1348
1349
        if (false != $result) {
1350
            if ($result['qty'] > 0) {
1351
                return true;
1352
            }
1353
        }
1354
1355
        return false;
1356
    }
1357
1358
    /**
1359
     * Check if a skill is searched.
1360
     *
1361
     * @param int $id The skill id
1362
     *
1363
     * @return bool Whether el skill is searched return true. Otherwise return false
1364
     */
1365
    public static function isSearched($id)
1366
    {
1367
        $id = (int) $id;
1368
1369
        if (empty($id)) {
1370
            return false;
1371
        }
1372
1373
        $skillRelProfileTable = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
1374
1375
        $result = Database::select(
1376
            'COUNT( DISTINCT `skill_id`) AS qty',
1377
            $skillRelProfileTable,
1378
            [
1379
                'where' => [
1380
                    'skill_id = ?' => $id,
1381
                ],
1382
            ],
1383
            'first'
1384
        );
1385
1386
        if (empty($result)) {
1387
            return false;
1388
        }
1389
1390
        if ($result['qty'] > 0) {
1391
            return true;
1392
        }
1393
1394
        return false;
1395
    }
1396
1397
    /**
1398
     * Get the achieved skills by course.
1399
     *
1400
     * @param int $courseId The course id
1401
     *
1402
     * @return array The skills list
1403
     */
1404
    public function listAchievedByCourse($courseId)
1405
    {
1406
        $courseId = (int) $courseId;
1407
1408
        if (0 == $courseId) {
1409
            return [];
1410
        }
1411
1412
        $list = [];
1413
1414
        $sql = "SELECT
1415
                    course.id c_id,
1416
                    course.title c_name,
1417
                    course.directory c_directory,
1418
                    user.id as user_id,
1419
                    user.lastname,
1420
                    user.firstname,
1421
                    user.username,
1422
                    skill.id skill_id,
1423
                    skill.name skill_name,
1424
                    sru.acquired_skill_at
1425
                FROM {$this->table_skill_rel_user} AS sru
1426
                INNER JOIN {$this->table_course}
1427
                ON sru.course_id = course.id
1428
                INNER JOIN {$this->table_user}
1429
                ON sru.user_id = user.id
1430
                INNER JOIN {$this->table}
1431
                ON sru.skill_id = skill.id
1432
                WHERE course.id = $courseId";
1433
1434
        $result = Database::query($sql);
1435
1436
        while ($row = Database::fetch_assoc($result)) {
1437
            $row['skill_name'] = self::translateName($row['skill_name']);
1438
            $list[] = $row;
1439
        }
1440
1441
        return $list;
1442
    }
1443
1444
    /**
1445
     * Get the users list who achieved a skill.
1446
     *
1447
     * @param int $skillId The skill id
1448
     *
1449
     * @return array The users list
1450
     */
1451
    public function listUsersWhoAchieved($skillId)
1452
    {
1453
        $skillId = (int) $skillId;
1454
1455
        if (0 == $skillId) {
1456
            return [];
1457
        }
1458
1459
        $list = [];
1460
        $sql = "SELECT
1461
                    course.id c_id,
1462
                    course.title c_name,
1463
                    course.directory c_directory,
1464
                    user.id as user_id,
1465
                    user.lastname,
1466
                    user.firstname,
1467
                    user.username,
1468
                    skill.id skill_id,
1469
                    skill.name skill_name,
1470
                    sru.acquired_skill_at
1471
                FROM {$this->table_skill_rel_user} AS sru
1472
                INNER JOIN {$this->table_course}
1473
                ON sru.course_id = course.id
1474
                INNER JOIN {$this->table_user}
1475
                ON sru.user_id = user.id
1476
                INNER JOIN {$this->table}
1477
                ON sru.skill_id = skill.id
1478
                WHERE skill.id = $skillId ";
1479
1480
        $result = Database::query($sql);
1481
        while ($row = Database::fetch_assoc($result)) {
1482
            $row['skill_name'] = self::translateName($row['skill_name']);
1483
            $list[] = $row;
1484
        }
1485
1486
        return $list;
1487
    }
1488
1489
    /**
1490
     * Get the session list where the user can achieve a skill.
1491
     *
1492
     * @param int $skillId The skill id
1493
     *
1494
     * @return array
1495
     */
1496
    public function getSessionsBySkill($skillId)
1497
    {
1498
        $skillId = (int) $skillId;
1499
1500
        $sql = "SELECT s.id, s.name
1501
                FROM {$this->table_gradebook} g
1502
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1503
                ON g.id = sg.gradebook_id
1504
                INNER JOIN {$this->sessionTable} s
1505
                ON g.session_id = s.id
1506
                WHERE sg.skill_id = $skillId
1507
                AND g.session_id > 0";
1508
1509
        $result = Database::query($sql);
1510
1511
        return Database::store_result($result, 'ASSOC');
1512
    }
1513
1514
    /**
1515
     * Check if the $fromUser can comment the $toUser skill issue.
1516
     *
1517
     * @param User $fromUser
1518
     * @param User $toUser
1519
     *
1520
     * @return bool
1521
     */
1522
    public static function userCanAddFeedbackToUser($fromUser, $toUser)
1523
    {
1524
        if (api_is_platform_admin()) {
1525
            return true;
1526
        }
1527
1528
        $userRepo = UserManager::getRepository();
1529
        $fromUserStatus = $fromUser->getStatus();
1530
1531
        switch ($fromUserStatus) {
1532
            case SESSIONADMIN:
1533
                if ('true' === api_get_setting('allow_session_admins_to_manage_all_sessions')) {
1534
                    if ($toUser->getCreatorId() === $fromUser->getId()) {
1535
                        return true;
1536
                    }
1537
                }
1538
1539
                $sessionAdmins = $userRepo->getSessionAdmins($toUser);
1540
1541
                foreach ($sessionAdmins as $sessionAdmin) {
1542
                    if ($sessionAdmin->getId() !== $fromUser->getId()) {
1543
                        continue;
1544
                    }
1545
1546
                    return true;
1547
                }
1548
                break;
1549
            case STUDENT_BOSS:
1550
                $studentBosses = $userRepo->getStudentBosses($toUser);
1551
                foreach ($studentBosses as $studentBoss) {
1552
                    if ($studentBoss->getId() !== $fromUser->getId()) {
1553
                        continue;
1554
                    }
1555
1556
                    return true;
1557
                }
1558
                break;
1559
            case DRH:
1560
                return UserManager::is_user_followed_by_drh(
1561
                    $toUser->getId(),
1562
                    $fromUser->getId()
1563
                );
1564
        }
1565
1566
        return false;
1567
    }
1568
1569
    /**
1570
     * If $studentId is set then check if current user has the right to see
1571
     * the page.
1572
     *
1573
     * @param int  $studentId check if current user has access to see $studentId
1574
     * @param bool $blockPage raise a api_not_allowed()
1575
     *
1576
     * @return bool
1577
     */
1578
    public static function isAllowed($studentId = 0, $blockPage = true)
1579
    {
1580
        $allowHR = 'true' === api_get_setting('allow_hr_skills_management');
1581
1582
        if (self::isToolAvailable()) {
1583
            if (api_is_platform_admin(false, $allowHR)) {
1584
                return true;
1585
            }
1586
1587
            if (!empty($studentId)) {
1588
                $currentUserId = api_get_user_id();
1589
                if ((int) $currentUserId === (int) $studentId) {
1590
                    return true;
1591
                }
1592
1593
                $haveAccess = self::hasAccessToUserSkill(
1594
                    $currentUserId,
1595
                    $studentId
1596
                );
1597
1598
                if ($haveAccess) {
1599
                    return true;
1600
                }
1601
            }
1602
        }
1603
1604
        if ($blockPage) {
1605
            api_not_allowed(true);
1606
        }
1607
1608
        return false;
1609
    }
1610
1611
    /**
1612
     * @return bool
1613
     */
1614
    public static function isToolAvailable()
1615
    {
1616
        $allowTool = api_get_setting('allow_skills_tool');
1617
1618
        if ('true' === $allowTool) {
1619
            return true;
1620
        }
1621
1622
        return false;
1623
    }
1624
1625
    /**
1626
     * @param int $currentUserId
1627
     * @param int $studentId
1628
     *
1629
     * @return bool
1630
     */
1631
    public static function hasAccessToUserSkill($currentUserId, $studentId)
1632
    {
1633
        if (self::isToolAvailable()) {
1634
            if (api_is_platform_admin()) {
1635
                return true;
1636
            }
1637
1638
            $currentUserId = (int) $currentUserId;
1639
            $studentId = (int) $studentId;
1640
1641
            if ($currentUserId === $studentId) {
1642
                return true;
1643
            }
1644
1645
            if (api_is_student_boss()) {
1646
                $isBoss = UserManager::userIsBossOfStudent($currentUserId, $studentId);
1647
                if ($isBoss) {
1648
                    return true;
1649
                }
1650
            }
1651
1652
            $allow = api_get_configuration_value('allow_private_skills');
1653
            if (true === $allow) {
1654
                if (api_is_teacher()) {
1655
                    return UserManager::isTeacherOfStudent(
1656
                        $currentUserId,
1657
                        $studentId
1658
                    );
1659
                }
1660
1661
                if (api_is_drh()) {
1662
                    return UserManager::is_user_followed_by_drh(
1663
                        $studentId,
1664
                        $currentUserId
1665
                    );
1666
                }
1667
            }
1668
        }
1669
1670
        return false;
1671
    }
1672
1673
    /**
1674
     * Get skills.
1675
     *
1676
     * @param int $userId
1677
     * @param int level
1678
     *
1679
     * @return array
1680
     */
1681
    public function getStudentSkills($userId, $level = 0)
1682
    {
1683
        $userId = (int) $userId;
1684
1685
        $sql = "SELECT s.id, s.name, sru.acquired_skill_at
1686
                FROM {$this->table} s
1687
                INNER JOIN {$this->table_skill_rel_user} sru
1688
                ON s.id = sru.skill_id
1689
                WHERE sru.user_id = $userId";
1690
1691
        $result = Database::query($sql);
1692
1693
        $skills = [];
1694
        foreach ($result as $item) {
1695
            if (empty($level)) {
1696
                $skills[] = [
1697
                    'name' => self::translateName($item['name']),
1698
                    'acquired_skill_at' => $item['acquired_skill_at'],
1699
                ];
1700
            } else {
1701
                $parents = self::get_parents($item['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method SkillModel::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

1701
                /** @scrutinizer ignore-call */ 
1702
                $parents = self::get_parents($item['id']);
Loading history...
1702
                // +2 because it takes into account the root
1703
                if (count($parents) == $level + 1) {
1704
                    $skills[] = [
1705
                        'name' => self::translateName($item['name']),
1706
                        'acquired_skill_at' => $item['acquired_skill_at'],
1707
                    ];
1708
                }
1709
            }
1710
        }
1711
1712
        return $skills;
1713
    }
1714
1715
    /**
1716
     * @param string $name
1717
     *
1718
     * @return string
1719
     */
1720
    public static function translateName($name)
1721
    {
1722
        $variable = ChamiloApi::getLanguageVar($name, 'Skill');
1723
1724
        return isset($GLOBALS[$variable]) ? $GLOBALS[$variable] : $name;
1725
    }
1726
1727
    /**
1728
     * @param string $code
1729
     *
1730
     * @return mixed|string
1731
     */
1732
    public static function translateCode($code)
1733
    {
1734
        if (empty($code)) {
1735
            return '';
1736
        }
1737
1738
        $variable = ChamiloApi::getLanguageVar($code, 'SkillCode');
1739
1740
        return isset($GLOBALS[$variable]) ? $GLOBALS[$variable] : $code;
1741
    }
1742
1743
    /**
1744
     * @param array $skillInfo
1745
     *
1746
     * @return array
1747
     */
1748
    public function setForm(FormValidator &$form, $skillInfo = [])
1749
    {
1750
        $allSkills = $this->getAllSkills();
1751
        $objGradebook = new Gradebook();
1752
1753
        $skillList = [0 => get_lang('None')];
1754
1755
        foreach ($allSkills as $skill) {
1756
            if (isset($skillInfo['id']) && $skill['id'] == $skillInfo['id']) {
1757
                continue;
1758
            }
1759
1760
            $skillList[$skill['id']] = $skill['name'];
1761
        }
1762
1763
        $allGradeBooks = $objGradebook->find('all');
1764
1765
        // This procedure is for check if there is already a Skill with no Parent (Root by default)
1766
        $gradeBookList = [];
1767
        foreach ($allGradeBooks as $gradebook) {
1768
            $gradeBookList[$gradebook['id']] = $gradebook['name'];
1769
        }
1770
1771
        $translateUrl = api_get_path(WEB_CODE_PATH).'admin/skill_translate.php?';
1772
        $translateNameButton = '';
1773
        $translateCodeButton = '';
1774
        $skillId = null;
1775
        if (!empty($skillInfo)) {
1776
            $skillId = $skillInfo['id'];
1777
            $translateNameUrl = $translateUrl.http_build_query(['skill' => $skillId, 'action' => 'name']);
1778
            $translateCodeUrl = $translateUrl.http_build_query(['skill' => $skillId, 'action' => 'code']);
1779
            $translateNameButton = Display::toolbarButton(
1780
                get_lang('Translate this term'),
1781
                $translateNameUrl,
1782
                'language',
1783
                'link'
1784
            );
1785
            $translateCodeButton = Display::toolbarButton(
1786
                get_lang('Translate this term'),
1787
                $translateCodeUrl,
1788
                'language',
1789
                'link'
1790
            );
1791
        }
1792
1793
        $form->addText('name', [get_lang('Name'), $translateNameButton], true, ['id' => 'name']);
1794
        $form->addText('short_code', [get_lang('Short code'), $translateCodeButton], false, ['id' => 'short_code']);
1795
1796
        // Cannot change parent of root
1797
        if (1 != $skillId) {
1798
            $form->addSelect('parent_id', get_lang('Parent'), $skillList, ['id' => 'parent_id']);
1799
        }
1800
1801
        $form->addSelect(
1802
            'gradebook_id',
1803
            [get_lang('Assessments'), get_lang('With Certificate')],
1804
            $gradeBookList,
1805
            ['id' => 'gradebook_id', 'multiple' => 'multiple', 'size' => 10]
1806
        );
1807
        $form->addTextarea('description', get_lang('Description'), ['id' => 'description', 'rows' => 7]);
1808
        $form->addTextarea('criteria', get_lang('Criteria to earn the badge'), ['id' => 'criteria', 'rows' => 7]);
1809
1810
        // EXTRA FIELDS
1811
        $extraField = new ExtraField('skill');
1812
        $returnParams = $extraField->addElements($form, $skillId);
1813
1814
        if (empty($skillInfo)) {
1815
            $form->addButtonCreate(get_lang('Add'));
1816
        } else {
1817
            $form->addButtonUpdate(get_lang('Update'));
1818
            $form->addHidden('id', $skillInfo['id']);
1819
        }
1820
1821
        return $returnParams;
1822
    }
1823
1824
    /**
1825
     * @return string
1826
     */
1827
    public function getToolBar()
1828
    {
1829
        $toolbar = Display::url(
1830
            Display::return_icon(
1831
                'back.png',
1832
                get_lang('Manage skills'),
1833
                null,
1834
                ICON_SIZE_MEDIUM
1835
            ),
1836
            api_get_path(WEB_CODE_PATH).'admin/skill_list.php'
1837
        );
1838
1839
        return Display::toolbarAction('skills_toolbar', [$toolbar]);
1840
    }
1841
1842
    /**
1843
     * @param SkillRelItem        $skillRelItem
1844
     * @param SkillRelItemRelUser $skillRelItemRelUser
1845
     * @param bool                $addHeader
1846
     *
1847
     * @return string
1848
     */
1849
    public static function getUserSkillStatusLabel($skillRelItem, $skillRelItemRelUser, $addHeader = true)
1850
    {
1851
        if (empty($skillRelItem)) {
1852
            return '';
1853
        }
1854
        $type = 'success';
1855
        if (empty($skillRelItemRelUser)) {
1856
            $type = 'danger';
1857
        }
1858
        $label = '';
1859
        $skill = $skillRelItem->getSkill();
1860
        if ($addHeader) {
1861
            $label .= '<span id="'.$skill->getId().'" class="user_skill" style="cursor:pointer">';
1862
        }
1863
        $label .= Display::label($skill->getName(), $type);
1864
        if ($addHeader) {
1865
            $label .= '</span>&nbsp;';
1866
        }
1867
1868
        return $label;
1869
    }
1870
1871
    /**
1872
     * Assign a user with a SkilRelItem object.
1873
     *
1874
     * @param int $typeId see ITEM_TYPE_* constants
1875
     * @param int $itemId
1876
     * @param int $userId
1877
     */
1878
    public static function addSkillsToUserForm(
1879
        FormValidator $form,
1880
        $typeId,
1881
        $itemId,
1882
        $userId,
1883
        $resultId = 0,
1884
        $addHeader = false
1885
    ) {
1886
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
1887
        if ($allowSkillInTools && !empty($typeId) && !empty($itemId) && !empty($userId)) {
1888
            $em = Database::getManager();
1889
            $items = $em->getRepository(SkillRelItem::class)->findBy(
1890
                ['itemId' => $itemId, 'itemType' => $typeId]
1891
            );
1892
1893
            $skillRelUser = new SkillRelUserModel();
1894
            $skillUserList = $skillRelUser->getUserSkills($userId);
1895
            if (!empty($skillUserList)) {
1896
                $skillUserList = array_column($skillUserList, 'skill_id');
1897
            }
1898
1899
            $skills = '';
1900
            /** @var SkillRelItem $skillRelItem */
1901
            foreach ($items as $skillRelItem) {
1902
                $criteria = [
1903
                    'user' => $userId,
1904
                    'skillRelItem' => $skillRelItem,
1905
                ];
1906
                $skillRelItemRelUser = $em->getRepository(SkillRelItemRelUser::class)->findOneBy($criteria);
1907
                $skills .= self::getUserSkillStatusLabel($skillRelItem, $skillRelItemRelUser);
1908
            }
1909
1910
            if (!empty($skills)) {
1911
                $url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=update_skill_rel_user&'.api_get_cidreq();
1912
                $params = [
1913
                    'item_id' => $itemId,
1914
                    'type_id' => $typeId,
1915
                    'user_id' => $userId,
1916
                    'course_id' => api_get_course_int_id(),
1917
                    'session_id' => api_get_session_id(),
1918
                    'result_id' => $resultId,
1919
                ];
1920
                $params = json_encode($params);
1921
                if ($addHeader) {
1922
                    $form->addHtml(Display::page_subheader2(get_lang('Skills')));
1923
                }
1924
                $html = '
1925
                <script>
1926
                    $(function() {
1927
                        $(".user_skill").on("click", function() {
1928
                            var skillId = this.id;
1929
                            var params = '.$params.';
1930
                            $.ajax({
1931
                                type: "GET",
1932
                                async: false,
1933
                                data: params,
1934
                                url: "'.$url.'&skill_id="+skillId,
1935
                                success: function(result) {
1936
                                    $("#" +skillId+ ".user_skill").html(result);
1937
                                }
1938
                            });
1939
                        });
1940
                    });
1941
                </script>
1942
                ';
1943
                $form->addHtml($html);
1944
                $form->addLabel(get_lang('Skills'), $skills);
1945
                if ($addHeader) {
1946
                    $form->addHtml('<br />');
1947
                }
1948
            }
1949
        }
1950
    }
1951
1952
    /**
1953
     * Add skills select ajax for an item (exercise, lp).
1954
     *
1955
     * @param int $typeId see ITEM_TYPE_* constants
1956
     * @param int $itemId
1957
     *
1958
     * @throws Exception
1959
     *
1960
     * @return array
1961
     */
1962
    public static function addSkillsToForm(FormValidator $form, $typeId, $itemId = 0)
1963
    {
1964
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
1965
        if (!$allowSkillInTools) {
1966
            return [];
1967
        }
1968
1969
        $skillList = [];
1970
        if (!empty($itemId)) {
1971
            $em = Database::getManager();
1972
            $items = $em->getRepository(SkillRelItem::class)->findBy(
1973
                ['itemId' => $itemId, 'itemType' => $typeId]
1974
            );
1975
            /** @var SkillRelItem $skillRelItem */
1976
            foreach ($items as $skillRelItem) {
1977
                $skillList[$skillRelItem->getSkill()->getId()] = $skillRelItem->getSkill()->getName();
1978
            }
1979
        }
1980
1981
        $courseId = api_get_course_int_id();
1982
        $sessionId = api_get_session_id();
1983
1984
        $url = api_get_path(WEB_AJAX_PATH).
1985
            'skill.ajax.php?a=search_skills_in_course&course_id='.$courseId.'&session_id='.$sessionId;
1986
        $form->addSelectAjax(
1987
            'skills',
1988
            get_lang('Skills'),
1989
            $skillList,
1990
            [
1991
                'url' => $url,
1992
                'multiple' => 'multiple',
1993
            ]
1994
        );
1995
1996
        return $skillList;
1997
    }
1998
1999
    /**
2000
     * @param int $courseId
2001
     * @param int $sessionId
2002
     *
2003
     * @return array
2004
     */
2005
    public static function getSkillRelItemsPerCourse($courseId, $sessionId = null)
2006
    {
2007
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2008
        $skills = [];
2009
2010
        if (empty($sessionId)) {
2011
            $sessionId = null;
2012
        }
2013
2014
        if ($allowSkillInTools) {
2015
            $em = Database::getManager();
2016
            $skills = $em->getRepository(SkillRelItem::class)->findBy(
2017
                ['courseId' => $courseId, 'sessionId' => $sessionId]
2018
            );
2019
        }
2020
2021
        return $skills;
2022
    }
2023
2024
    /**
2025
     * @param int $itemId
2026
     * @param int $itemType
2027
     *
2028
     * @return array
2029
     */
2030
    public static function getItemInfo($itemId, $itemType)
2031
    {
2032
        $itemInfo = [];
2033
        $itemId = (int) $itemId;
2034
        $itemType = (int) $itemType;
2035
        $em = Database::getManager();
2036
2037
        switch ($itemType) {
2038
            case ITEM_TYPE_EXERCISE:
2039
                /** @var CQuiz $item */
2040
                $item = $em->getRepository(CQuiz::class)->find($itemId);
2041
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CQuiz, thus it always evaluated to true.
Loading history...
2042
                    $itemInfo['name'] = $item->getTitle();
2043
                }
2044
                break;
2045
            case ITEM_TYPE_HOTPOTATOES:
2046
                break;
2047
            case ITEM_TYPE_LINK:
2048
                /** @var CLink $item */
2049
                $item = $em->getRepository(CLink::class)->find($itemId);
2050
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CLink, thus it always evaluated to true.
Loading history...
2051
                    $itemInfo['name'] = $item->getTitle();
2052
                }
2053
                break;
2054
            case ITEM_TYPE_LEARNPATH:
2055
                /** @var CLp $item */
2056
                $item = $em->getRepository(CLp::class)->find($itemId);
2057
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CLp, thus it always evaluated to true.
Loading history...
2058
                    $itemInfo['name'] = $item->getName();
2059
                }
2060
                break;
2061
            case ITEM_TYPE_GRADEBOOK:
2062
                break;
2063
            case ITEM_TYPE_STUDENT_PUBLICATION:
2064
                /** @var CStudentPublication $item */
2065
                $item = $em->getRepository(CStudentPublication::class)->find($itemId);
2066
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CStudentPublication, thus it always evaluated to true.
Loading history...
2067
                    $itemInfo['name'] = $item->getTitle();
2068
                }
2069
                break;
2070
            //ITEM_TYPE_FORUM', 7);
2071
            case ITEM_TYPE_ATTENDANCE:
2072
                /** @var CAttendance $item */
2073
                $item = $em->getRepository(CAttendance::class)->find($itemId);
2074
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CAttendance, thus it always evaluated to true.
Loading history...
2075
                    $itemInfo['name'] = $item->getName();
2076
                }
2077
                break;
2078
            case ITEM_TYPE_SURVEY:
2079
                /** @var CSurvey $item */
2080
                $item = $em->getRepository(CSurvey::class)->find($itemId);
2081
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CSurvey, thus it always evaluated to true.
Loading history...
2082
                    $itemInfo['name'] = strip_tags($item->getTitle());
2083
                }
2084
                break;
2085
            case ITEM_TYPE_FORUM_THREAD:
2086
                /** @var CForumThread $item */
2087
                $item = $em->getRepository(CForumThread::class)->find($itemId);
2088
                if ($item) {
0 ignored issues
show
introduced by
$item is of type Chamilo\CourseBundle\Entity\CForumThread, thus it always evaluated to true.
Loading history...
2089
                    $itemInfo['name'] = $item->getThreadTitle();
2090
                }
2091
                break;
2092
        }
2093
2094
        return $itemInfo;
2095
    }
2096
2097
    /**
2098
     * @param int $typeId
2099
     * @param int $itemId
2100
     *
2101
     * @return array
2102
     */
2103
    public static function getSkillRelItems($typeId, $itemId)
2104
    {
2105
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2106
        $skills = [];
2107
        if ($allowSkillInTools) {
2108
            $em = Database::getManager();
2109
            $skills = $em->getRepository(SkillRelItem::class)->findBy(
2110
                ['itemId' => $itemId, 'itemType' => $typeId]
2111
            );
2112
        }
2113
2114
        return $skills;
2115
    }
2116
2117
    /**
2118
     * @param int $typeId
2119
     * @param int $itemId
2120
     *
2121
     * @return string
2122
     */
2123
    public static function getSkillRelItemsToString($typeId, $itemId)
2124
    {
2125
        $skills = self::getSkillRelItems($typeId, $itemId);
2126
        $skillToString = '';
2127
        if (!empty($skills)) {
2128
            /** @var SkillRelItem $skillRelItem */
2129
            $skillList = [];
2130
            foreach ($skills as $skillRelItem) {
2131
                $skillList[] = Display::label($skillRelItem->getSkill()->getName(), 'success');
2132
            }
2133
            $skillToString = '&nbsp;'.implode(' ', $skillList);
2134
        }
2135
2136
        return $skillToString;
2137
    }
2138
2139
    /**
2140
     * @param int $itemId
2141
     * @param int $typeId
2142
     */
2143
    public static function deleteSkillsFromItem($itemId, $typeId)
2144
    {
2145
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2146
        if ($allowSkillInTools) {
2147
            $itemId = (int) $itemId;
2148
            $typeId = (int) $typeId;
2149
2150
            $em = Database::getManager();
2151
            // Delete old ones
2152
            $items = $em->getRepository(SkillRelItem::class)->findBy(
2153
                ['itemId' => $itemId, 'itemType' => $typeId]
2154
            );
2155
2156
            /** @var SkillRelItem $skillRelItem */
2157
            foreach ($items as $skillRelItem) {
2158
                $em->remove($skillRelItem);
2159
            }
2160
            $em->flush();
2161
        }
2162
    }
2163
2164
    /**
2165
     * Relate skill with an item (exercise, gradebook, lp, etc).
2166
     *
2167
     * @param FormValidator $form
2168
     * @param int           $typeId
2169
     * @param int           $itemId
2170
     *
2171
     * @throws \Doctrine\ORM\OptimisticLockException
2172
     */
2173
    public static function saveSkills($form, $typeId, $itemId)
2174
    {
2175
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2176
        if ($allowSkillInTools) {
2177
            $userId = api_get_user_id();
2178
            $courseId = api_get_course_int_id();
2179
            if (empty($courseId)) {
2180
                $courseId = null;
2181
            }
2182
            $sessionId = api_get_session_id();
2183
            if (empty($sessionId)) {
2184
                $sessionId = null;
2185
            }
2186
2187
            $em = Database::getManager();
2188
            $skills = (array) $form->getSubmitValue('skills');
2189
2190
            // Delete old ones
2191
            $items = $em->getRepository(SkillRelItem::class)->findBy(
2192
                ['itemId' => $itemId, 'itemType' => $typeId]
2193
            );
2194
            if (!empty($items)) {
2195
                /** @var SkillRelItem $skillRelItem */
2196
                foreach ($items as $skillRelItem) {
2197
                    if (!in_array($skillRelItem->getSkill()->getId(), $skills)) {
2198
                        $em->remove($skillRelItem);
2199
                    }
2200
                }
2201
                $em->flush();
2202
            }
2203
2204
            // Add new one
2205
            if (!empty($skills)) {
2206
                foreach ($skills as $skillId) {
2207
                    /** @var Skill $skill */
2208
                    $skill = $em->getRepository(Skill::class)->find($skillId);
2209
                    if ($skill) {
2210
                        if (!$skill->hasItem($typeId, $itemId)) {
2211
                            $skillRelItem = new SkillRelItem();
2212
                            $skillRelItem
2213
                                ->setItemType($typeId)
2214
                                ->setItemId($itemId)
2215
                                ->setCourseId($courseId)
2216
                                ->setSessionId($sessionId)
2217
                                ->setCreatedBy($userId)
2218
                                ->setUpdatedBy($userId)
2219
                            ;
2220
                            $skill->addItem($skillRelItem);
2221
                            $em->persist($skill);
2222
                            $em->flush();
2223
                        }
2224
                    }
2225
                }
2226
            }
2227
        }
2228
    }
2229
2230
    /**
2231
     * Relate skill with an item (exercise, gradebook, lp, etc).
2232
     *
2233
     * @return bool
2234
     */
2235
    public static function saveSkillsToCourseFromForm(FormValidator $form)
2236
    {
2237
        $skills = (array) $form->getSubmitValue('skills');
2238
        $courseId = (int) $form->getSubmitValue('course_id');
2239
        $sessionId = $form->getSubmitValue('session_id');
2240
2241
        return self::saveSkillsToCourse($skills, $courseId, $sessionId);
2242
    }
2243
2244
    /**
2245
     * @param array $skills
2246
     * @param int   $courseId
2247
     * @param int   $sessionId
2248
     *
2249
     * @throws \Doctrine\ORM\OptimisticLockException
2250
     *
2251
     * @return bool
2252
     */
2253
    public static function saveSkillsToCourse($skills, $courseId, $sessionId)
2254
    {
2255
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2256
        if (!$allowSkillInTools) {
2257
            return false;
2258
        }
2259
2260
        $em = Database::getManager();
2261
        $sessionId = empty($sessionId) ? null : (int) $sessionId;
2262
2263
        $course = api_get_course_entity($courseId);
2264
        if (empty($course)) {
2265
            return false;
2266
        }
2267
        $session = null;
2268
        if (!empty($sessionId)) {
2269
            $session = api_get_session_entity($sessionId);
2270
            $courseExistsInSession = SessionManager::sessionHasCourse($sessionId, $course->getCode());
2271
            if (!$courseExistsInSession) {
2272
                return false;
2273
            }
2274
        }
2275
2276
        // Delete old ones
2277
        $items = $em->getRepository(SkillRelCourse::class)->findBy(
2278
            ['course' => $courseId, 'session' => $sessionId]
2279
        );
2280
2281
        if (!empty($items)) {
2282
            /** @var SkillRelCourse $item */
2283
            foreach ($items as $item) {
2284
                if (!in_array($item->getSkill()->getId(), $skills)) {
2285
                    $em->remove($item);
2286
                }
2287
            }
2288
            $em->flush();
2289
        }
2290
2291
        // Add new one
2292
        if (!empty($skills)) {
2293
            foreach ($skills as $skillId) {
2294
                $item = new SkillRelCourse();
2295
                $item->setCourse($course);
2296
                $item->setSession($session);
2297
2298
                /** @var Skill $skill */
2299
                $skill = $em->getRepository(Skill::class)->find($skillId);
2300
                if ($skill) {
2301
                    if (!$skill->hasCourseAndSession($item)) {
2302
                        $skill->addToCourse($item);
2303
                        $em->persist($skill);
2304
                    }
2305
                }
2306
            }
2307
            $em->flush();
2308
        }
2309
2310
        return true;
2311
    }
2312
2313
    /**
2314
     * Get the icon (badge image) URL.
2315
     */
2316
    public static function getWebIconPath(?Skill $skill): string
2317
    {
2318
        $default = \Display::return_icon('badges-default.png', null, null, ICON_SIZE_HUGE, null, true);
2319
2320
        if (null === $skill) {
2321
            return $default;
2322
        }
2323
2324
        if (!$skill->hasAsset()) {
2325
            return $default;
2326
        }
2327
2328
        return Container::getAssetRepository()->getAssetUrl($skill->getAsset());
2329
    }
2330
2331
    /**
2332
     * @param User   $user
2333
     * @param Skill  $skill
2334
     * @param int    $levelId
2335
     * @param string $argumentation
2336
     * @param int    $authorId
2337
     *
2338
     * @return SkillRelUser|null
2339
     */
2340
    public function addSkillToUserBadge($user, $skill, $levelId, $argumentation, $authorId)
2341
    {
2342
        $showLevels = false === api_get_configuration_value('hide_skill_levels');
2343
2344
        $entityManager = Database::getManager();
2345
2346
        $skillUserRepo = $entityManager->getRepository(SkillRelUser::class);
2347
2348
        $criteria = ['user' => $user, 'skill' => $skill];
2349
        $result = $skillUserRepo->findOneBy($criteria);
2350
2351
        if (null !== $result) {
2352
            return null;
2353
        }
2354
2355
        $skillLevelRepo = $entityManager->getRepository(Level::class);
2356
2357
        $skillUser = (new SkillRelUser())
2358
            ->setUser($user)
2359
            ->setSkill($skill)
2360
            ->setArgumentation($argumentation)
2361
            ->setArgumentationAuthorId($authorId)
2362
            ->setAssignedBy(0)
2363
        ;
2364
2365
        if ($showLevels && !empty($levelId)) {
2366
            $level = $skillLevelRepo->find($levelId);
2367
            $skillUser->setAcquiredLevel($level);
2368
        }
2369
2370
        $entityManager->persist($skillUser);
2371
        $entityManager->flush();
2372
2373
        return $skillUser;
2374
    }
2375
}
2376