Passed
Push — master ( 18111a...f46ab8 )
by Julito
12:19
created

SkillModel::getSkillsTree()   D

Complexity

Conditions 26
Paths 96

Size

Total Lines 146
Code Lines 79

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 26
eloc 79
c 0
b 0
f 0
nc 96
nop 4
dl 0
loc 146
rs 4.1666

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

317
                /** @scrutinizer ignore-call */ 
318
                $skillInfo = self::get($row['id']);
Loading history...
318
319
                $row['img_mini'] = $skillInfo['img_mini'];
320
                $row['img_big'] = $skillInfo['img_big'];
321
                $row['img_small'] = $skillInfo['img_small'];
322
323
                $row['name'] = self::translateName($row['name']);
324
                $row['short_code'] = self::translateCode($row['short_code']);
325
                $skillRelSkill = new SkillRelSkillModel();
326
                $parents = $skillRelSkill->getSkillParents($row['id']);
327
                $row['level'] = count($parents) - 1;
328
                $row['gradebooks'] = $this->getGradebooksBySkill($row['id']);
329
                $skills[$row['id']] = $row;
330
            }
331
        }
332
333
        // Load all children of the parent_id
334
        if (!empty($skills) && !empty($parent_id)) {
335
            foreach ($skills as $skill) {
336
                $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

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

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

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

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
2310
            if (empty($skill->getIcon())) {
2311
                return \Display::return_icon('badges-default.png', null, null, ICON_SIZE_BIG, null, true);
2312
            }
2313
2314
            //return api_get_path(WEB_UPLOAD_PATH).'badges/'.sha1($skill->getName()).'-small.png';
2315
        }
2316
2317
        if (empty($skill->getIcon())) {
2318
            return \Display::return_icon('badges-default.png', null, null, ICON_SIZE_HUGE, null, true);
2319
        }
2320
2321
        //return api_get_path(WEB_UPLOAD_PATH)."badges/{$skill->getIcon()}";
2322
    }
2323
2324
    /**
2325
     * @param User   $user
2326
     * @param Skill  $skill
2327
     * @param int    $levelId
2328
     * @param string $argumentation
2329
     * @param int    $authorId
2330
     *
2331
     * @return \Chamilo\CoreBundle\Entity\SkillRelUser|bool
2332
     *
2333
     */
2334
    public function addSkillToUserBadge($user, $skill, $levelId, $argumentation, $authorId)
2335
    {
2336
        $showLevels = false === api_get_configuration_value('hide_skill_levels');
2337
2338
        $entityManager = Database::getManager();
2339
2340
        $skillUserRepo = $entityManager->getRepository(SkillRelUser::class);
2341
2342
        $criteria = ['user' => $user, 'skill' => $skill];
2343
        $result = $skillUserRepo->findOneBy($criteria);
2344
2345
        if (!empty($result)) {
2346
            return false;
2347
        }
2348
2349
        $skillLevelRepo = $entityManager->getRepository(Level::class);
2350
2351
        $skillUser = new SkillRelUser();
2352
        $skillUser->setUser($user);
2353
        $skillUser->setSkill($skill);
2354
2355
        if ($showLevels && !empty($levelId)) {
2356
            $level = $skillLevelRepo->find($levelId);
2357
            $skillUser->setAcquiredLevel($level);
2358
        }
2359
2360
        $skillUser->setArgumentation($argumentation);
2361
        $skillUser->setArgumentationAuthorId($authorId);
2362
        $skillUser->setAcquiredSkillAt(new DateTime());
2363
        $skillUser->setAssignedBy(0);
2364
2365
        $entityManager->persist($skillUser);
2366
        $entityManager->flush();
2367
2368
        return $skillUser;
2369
    }
2370
}
2371