Passed
Push — master ( 6ef08a...006406 )
by Julito
08:14
created

SkillModel::getSkillToJson()   B

Complexity

Conditions 9
Paths 8

Size

Total Lines 39
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

2389
        $htmlHeadXtra[] = '<script src="'./** @scrutinizer ignore-type */ $backpack.'issuer.js"></script>';
Loading history...
2390
    }
2391
2392
    public static function exportBadge(Skill $skill, SkillRelUser $skillRelUser, string $urlToRedirect)
2393
    {
2394
        if ($skill->hasAsset()) {
2395
            $assetRepo = Container::getAssetRepository();
2396
            $imageContent = $assetRepo->getAssetContent($skill->getAsset());
2397
        } else {
2398
            $defaultBadge = api_get_path(SYS_PUBLIC_PATH).'img/icons/128/badges-default.png';
2399
            $imageContent = file_get_contents($defaultBadge);
2400
        }
2401
2402
        $png = new PNGImageBaker($imageContent);
2403
2404
        if ($png->checkChunks('tEXt', 'openbadges')) {
2405
            $result = $png->addChunk('tEXt', 'openbadges', SkillRelUserModel::getAssertionUrl($skillRelUser));
2406
            $verifyBadge = $png->extractBadgeInfo($result);
2407
2408
            $error = true;
2409
            if (is_array($verifyBadge)) {
2410
                $error = false;
2411
            }
2412
2413
            if (!$error) {
2414
                header('Content-type: image/png');
2415
                echo $result;
2416
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2417
            }
2418
        }
2419
2420
        Display::addFlash(
2421
            Display::return_message(
2422
                get_lang(
2423
                    'There was a problem embedding the badge assertion info into the badge image, but you can still use this page as a valid proof.'
2424
                ),
2425
                'warning'
2426
            )
2427
        );
2428
        api_location($urlToRedirect);
2429
    }
2430
2431
}
2432