Passed
Push — master ( 979a1d...b582f4 )
by Angel Fernando Quiroz
17:40
created

SkillModel::translateName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

2383
        $htmlHeadXtra[] = '<script src="'./** @scrutinizer ignore-type */ $backpack.'issuer.js"></script>';
Loading history...
2384
    }
2385
2386
    public static function exportBadge(Skill $skill, SkillRelUser $skillRelUser, string $urlToRedirect)
2387
    {
2388
        if ($skill->hasAsset()) {
2389
            $assetRepo = Container::getAssetRepository();
2390
            $imageContent = $assetRepo->getAssetContent($skill->getAsset());
2391
        } else {
2392
            $defaultBadge = api_get_path(SYS_PUBLIC_PATH).'img/icons/128/badges-default.png';
2393
            $imageContent = file_get_contents($defaultBadge);
2394
        }
2395
2396
        $png = new PNGImageBaker($imageContent);
2397
2398
        if ($png->checkChunks('tEXt', 'openbadges')) {
2399
            $result = $png->addChunk('tEXt', 'openbadges', SkillRelUserModel::getAssertionUrl($skillRelUser));
2400
            $verifyBadge = $png->extractBadgeInfo($result);
2401
2402
            $error = true;
2403
            if (is_array($verifyBadge)) {
2404
                $error = false;
2405
            }
2406
2407
            if (!$error) {
2408
                //header('Content-type: image/png');
2409
                header('Content-type: application/octet-stream');
2410
                header('Content-Type: application/force-download');
2411
                header('Content-Disposition: attachment; filename= '.api_get_unique_id());
2412
2413
                echo $result;
2414
                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...
2415
            }
2416
        }
2417
2418
        Display::addFlash(
2419
            Display::return_message(
2420
                get_lang(
2421
                    'There was a problem embedding the badge assertion info into the badge image, but you can still use this page as a valid proof.'
2422
                ),
2423
                'warning'
2424
            )
2425
        );
2426
        api_location($urlToRedirect);
2427
    }
2428
2429
}
2430