Completed
Push — master ( 7bef58...5c053f )
by Julito
25:30
created

Skill::getItemInfo()   C

Complexity

Conditions 17
Paths 17

Size

Total Lines 65
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 45
nc 17
nop 2
dl 0
loc 65
rs 5.9044
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
5
use Chamilo\CoreBundle\Entity\Skill as SkillEntity;
6
use Chamilo\SkillBundle\Entity\SkillRelCourse;
7
use Chamilo\SkillBundle\Entity\SkillRelItem;
8
use Chamilo\UserBundle\Entity\Repository\UserRepository;
9
use Chamilo\UserBundle\Entity\User;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, User. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
10
use Fhaculty\Graph\Graph;
11
use Fhaculty\Graph\Vertex;
12
13
/**
14
 * Class SkillProfile.
15
 *
16
 * @package chamilo.library
17
 */
18
class SkillProfile extends Model
19
{
20
    public $columns = ['id', 'name', 'description'];
21
22
    /**
23
     * Constructor.
24
     */
25
    public function __construct()
26
    {
27
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_PROFILE);
28
        $this->table_rel_profile = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
0 ignored issues
show
Bug Best Practice introduced by
The property table_rel_profile does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
29
    }
30
31
    /**
32
     * @return array
33
     */
34
    public function getProfiles()
35
    {
36
        $sql = "SELECT * FROM $this->table p
37
                INNER JOIN $this->table_rel_profile sp
38
                ON(p.id = sp.profile_id) ";
39
        $result = Database::query($sql);
40
        $profiles = Database::store_result($result, 'ASSOC');
41
42
        return $profiles;
43
    }
44
45
    /**
46
     * This function is for editing profile info from profile_id.
47
     *
48
     * @param int    $profileId
49
     * @param string $name
50
     * @param string $description
51
     *
52
     * @return bool
53
     */
54
    public function updateProfileInfo($profileId, $name, $description)
55
    {
56
        $profileId = (int) $profileId;
57
58
        if (empty($profileId)) {
59
            return false;
60
        }
61
62
        $name = Database::escape_string($name);
63
        $description = Database::escape_string($description);
64
65
        $sql = "UPDATE $this->table SET
66
                    name = '$name',
67
                    description = '$description'
68
                WHERE id = $profileId ";
69
        Database::query($sql);
70
71
        return true;
72
    }
73
74
    /**
75
     * Call the save method of the parent class and the SkillRelProfile object.
76
     *
77
     * @param array $params
78
     * @param bool  $show_query Whether to show the query in parent save() method
79
     *
80
     * @return mixed Profile ID or false if incomplete params
81
     */
82
    public function save($params, $show_query = false)
83
    {
84
        if (!empty($params)) {
85
            $profile_id = parent::save($params, $show_query);
86
            if ($profile_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $profile_id of type integer|false is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
87
                $skill_rel_profile = new SkillRelProfile();
88
                if (isset($params['skills'])) {
89
                    foreach ($params['skills'] as $skill_id) {
90
                        $attributes = [
91
                            'skill_id' => $skill_id,
92
                            'profile_id' => $profile_id,
93
                        ];
94
                        $skill_rel_profile->save($attributes);
95
                    }
96
                }
97
98
                return $profile_id;
99
            }
100
        }
101
102
        return false;
103
    }
104
105
    /**
106
     * Delete a skill profile.
107
     *
108
     * @param int $id The skill profile id
109
     *
110
     * @return bool Whether delete a skill profile
111
     */
112
    public function delete($id)
113
    {
114
        Database::delete(
115
            $this->table_rel_profile,
116
            [
117
                'profile_id' => $id,
118
            ]
119
        );
120
121
        return parent::delete($id);
122
    }
123
}
124
125
/**
126
 * Class SkillRelProfile.
127
 */
128
class SkillRelProfile extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
129
{
130
    public $columns = ['id', 'skill_id', 'profile_id'];
131
132
    /**
133
     * Constructor.
134
     */
135
    public function __construct()
136
    {
137
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
138
        $this->tableProfile = Database::get_main_table(TABLE_MAIN_SKILL_PROFILE);
0 ignored issues
show
Bug Best Practice introduced by
The property tableProfile does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
139
    }
140
141
    /**
142
     * @param int $profileId
143
     *
144
     * @return array
145
     */
146
    public function getSkillsByProfile($profileId)
147
    {
148
        $profileId = intval($profileId);
149
        $skills = $this->get_all(['where' => ['profile_id = ? ' => $profileId]]);
150
        $return = [];
151
        if (!empty($skills)) {
152
            foreach ($skills as $skill_data) {
153
                $return[] = $skill_data['skill_id'];
154
            }
155
        }
156
157
        return $return;
158
    }
159
160
    /**
161
     * This function is for getting profile info from profile_id.
162
     *
163
     * @param int $profileId
164
     *
165
     * @return array
166
     */
167
    public function getProfileInfo($profileId)
168
    {
169
        $sql = "SELECT * FROM $this->table p
170
                INNER JOIN $this->tableProfile pr
171
                ON (pr.id = p.profile_id)
172
                WHERE p.profile_id = ".intval($profileId);
173
        $result = Database::query($sql);
174
        $profileData = Database::fetch_array($result, 'ASSOC');
175
176
        return $profileData;
177
    }
178
}
179
180
/**
181
 * Class SkillRelSkill.
182
 */
183
class SkillRelSkill extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
184
{
185
    public $columns = ['skill_id', 'parent_id', 'relation_type', 'level'];
186
187
    /**
188
     * Constructor.
189
     */
190
    public function __construct()
191
    {
192
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
193
        $this->tableSkill = Database::get_main_table(TABLE_MAIN_SKILL);
0 ignored issues
show
Bug Best Practice introduced by
The property tableSkill does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
194
    }
195
196
    /**
197
     * Gets an element.
198
     *
199
     * @param int $id
200
     *
201
     * @return array
202
     */
203
    public function getSkillInfo($id)
204
    {
205
        if (empty($id)) {
206
            return [];
207
        }
208
        $result = Database::select(
209
            '*',
210
            $this->table,
211
            ['where' => ['skill_id = ?' => intval($id)]],
212
            'first'
213
        );
214
215
        return $result;
216
    }
217
218
    /**
219
     * @param int  $skillId
220
     * @param bool $add_child_info
221
     *
222
     * @return array
223
     */
224
    public function getSkillParents($skillId, $add_child_info = true)
225
    {
226
        $skillId = intval($skillId);
227
        $sql = 'SELECT child.* FROM '.$this->table.' child
228
                LEFT JOIN '.$this->table.' parent
229
                ON child.parent_id = parent.skill_id
230
                WHERE child.skill_id = '.$skillId.' ';
231
        $result = Database::query($sql);
232
        $skill = Database::store_result($result, 'ASSOC');
233
        $skill = isset($skill[0]) ? $skill[0] : null;
234
235
        $parents = [];
236
        if (!empty($skill)) {
237
            if ($skill['parent_id'] != null) {
238
                $parents = self::getSkillParents($skill['parent_id']);
0 ignored issues
show
Bug Best Practice introduced by
The method SkillRelSkill::getSkillParents() is not static, but was called statically. ( Ignorable by Annotation )

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

238
                /** @scrutinizer ignore-call */ 
239
                $parents = self::getSkillParents($skill['parent_id']);
Loading history...
239
            }
240
            if ($add_child_info) {
241
                $parents[] = $skill;
242
            }
243
        }
244
245
        return $parents;
246
    }
247
248
    /**
249
     * @param int $skillId
250
     *
251
     * @return array
252
     */
253
    public function getDirectParents($skillId)
254
    {
255
        $skillId = (int) $skillId;
256
        $sql = 'SELECT parent_id as skill_id 
257
                FROM '.$this->table.'
258
                WHERE skill_id = '.$skillId;
259
        $result = Database::query($sql);
260
        $skill = Database::store_result($result, 'ASSOC');
261
        $skill = isset($skill[0]) ? $skill[0] : null;
262
        $parents = [];
263
        if (!empty($skill)) {
264
            $parents[] = $skill;
265
        }
266
267
        return $parents;
268
    }
269
270
    /**
271
     * @param int  $skill_id
272
     * @param bool $load_user_data
273
     * @param bool $user_id
274
     *
275
     * @return array
276
     */
277
    public function getChildren(
278
        $skill_id,
279
        $load_user_data = false,
280
        $user_id = false,
281
        $order = ''
0 ignored issues
show
Unused Code introduced by
The parameter $order is not used and could be removed. ( Ignorable by Annotation )

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

281
        /** @scrutinizer ignore-unused */ $order = ''

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
282
    ) {
283
        $skill_id = (int) $skill_id;
284
        $sql = 'SELECT parent.* FROM '.$this->tableSkill.' skill
285
                INNER JOIN '.$this->table.' parent
286
                ON parent.id = skill.id
287
                WHERE parent_id = '.$skill_id.'
288
                ORDER BY skill.name ASC';
289
        $result = Database::query($sql);
290
        $skills = Database::store_result($result, 'ASSOC');
291
292
        $skill_obj = new Skill();
293
        $skill_rel_user = new SkillRelUser();
294
295
        if ($load_user_data) {
296
            $passed_skills = $skill_rel_user->getUserSkills($user_id);
297
            $done_skills = [];
298
            foreach ($passed_skills as $done_skill) {
299
                $done_skills[] = $done_skill['skill_id'];
300
            }
301
        }
302
303
        if (!empty($skills)) {
304
            foreach ($skills as &$skill) {
305
                $skill['data'] = $skill_obj->get($skill['skill_id']);
306
                if (isset($skill['data']) && !empty($skill['data'])) {
307
                    if (!empty($done_skills)) {
308
                        $skill['data']['passed'] = 0;
309
                        if (in_array($skill['skill_id'], $done_skills)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $done_skills does not seem to be defined for all execution paths leading up to this point.
Loading history...
310
                            $skill['data']['passed'] = 1;
311
                        }
312
                    }
313
                } else {
314
                    $skill = null;
315
                }
316
            }
317
        }
318
319
        return $skills;
320
    }
321
322
    /**
323
     * @param array $params
324
     *
325
     * @return bool
326
     */
327
    public function updateBySkill($params)
328
    {
329
        $result = Database::update(
330
            $this->table,
331
            $params,
332
            ['skill_id = ? ' => $params['skill_id']]
333
        );
334
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type integer|false 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...
335
            return true;
336
        }
337
338
        return false;
339
    }
340
341
    /**
342
     * @param int $skill_id
343
     * @param int $parent_id
344
     *
345
     * @return bool
346
     */
347
    public function relationExists($skill_id, $parent_id)
348
    {
349
        $result = $this->find(
350
            'all',
351
            [
352
                'where' => [
353
                    'skill_id = ? AND parent_id = ?' => [
354
                        $skill_id,
355
                        $parent_id,
356
                    ],
357
                ],
358
            ]
359
        );
360
361
        if (!empty($result)) {
362
            return true;
363
        }
364
365
        return false;
366
    }
367
}
368
369
/**
370
 * Class SkillRelGradebook.
371
 */
372
class SkillRelGradebook extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
373
{
374
    public $columns = ['id', 'gradebook_id', 'skill_id'];
375
376
    /**
377
     * SkillRelGradebook constructor.
378
     */
379
    public function __construct()
380
    {
381
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
382
    }
383
384
    /**
385
     * @param int $gradebookId
386
     * @param int $skillId
387
     *
388
     * @return bool
389
     */
390
    public function existsGradeBookSkill($gradebookId, $skillId)
391
    {
392
        $result = $this->find(
393
            'all',
394
            [
395
                'where' => [
396
                    'gradebook_id = ? AND skill_id = ?' => [
397
                        $gradebookId,
398
                        $skillId,
399
                    ],
400
                ],
401
            ]
402
        );
403
        if (!empty($result)) {
404
            return true;
405
        }
406
407
        return false;
408
    }
409
410
    /**
411
     * Gets an element.
412
     */
413
    public function getSkillInfo($skill_id, $gradebookId)
414
    {
415
        if (empty($skill_id)) {
416
            return [];
417
        }
418
        $result = Database::select(
419
            '*',
420
            $this->table,
421
            [
422
                'where' => [
423
                    'skill_id = ? AND gradebook_id = ? ' => [
424
                        $skill_id,
425
                        $gradebookId,
426
                    ],
427
                ],
428
            ],
429
            'first'
430
        );
431
432
        return $result;
433
    }
434
435
    /**
436
     * @param int   $skill_id
437
     * @param array $gradebook_list
438
     */
439
    public function updateGradeBookListBySkill($skill_id, $gradebook_list)
440
    {
441
        $original_gradebook_list = $this->find(
442
            'all',
443
            ['where' => ['skill_id = ?' => [$skill_id]]]
444
        );
445
        $gradebooks_to_remove = [];
446
        $gradebooks_to_add = [];
447
        $original_gradebook_list_ids = [];
448
449
        if (!empty($original_gradebook_list)) {
450
            foreach ($original_gradebook_list as $gradebook) {
451
                if (!in_array($gradebook['gradebook_id'], $gradebook_list)) {
452
                    $gradebooks_to_remove[] = $gradebook['id'];
453
                }
454
            }
455
            foreach ($original_gradebook_list as $gradebook_item) {
456
                $original_gradebook_list_ids[] = $gradebook_item['gradebook_id'];
457
            }
458
        }
459
460
        if (!empty($gradebook_list)) {
461
            foreach ($gradebook_list as $gradebook_id) {
462
                if (!in_array($gradebook_id, $original_gradebook_list_ids)) {
463
                    $gradebooks_to_add[] = $gradebook_id;
464
                }
465
            }
466
        }
467
468
        if (!empty($gradebooks_to_remove)) {
469
            foreach ($gradebooks_to_remove as $id) {
470
                $this->delete($id);
471
            }
472
        }
473
474
        if (!empty($gradebooks_to_add)) {
475
            foreach ($gradebooks_to_add as $gradebook_id) {
476
                $attributes = [
477
                    'skill_id' => $skill_id,
478
                    'gradebook_id' => $gradebook_id,
479
                ];
480
                $this->save($attributes);
481
            }
482
        }
483
    }
484
485
    /**
486
     * @param array $params
487
     *
488
     * @return bool|void
489
     */
490
    public function updateBySkill($params)
491
    {
492
        $skillInfo = $this->existsGradeBookSkill(
493
            $params['gradebook_id'],
494
            $params['skill_id']
495
        );
496
497
        if ($skillInfo) {
498
            return;
499
        } else {
500
            $result = $this->save($params);
501
        }
502
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type integer|false 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...
503
            return true;
504
        }
505
506
        return false;
507
    }
508
}
509
510
/**
511
 * Class SkillRelUser.
512
 */
513
class SkillRelUser extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
514
{
515
    public $columns = [
516
        'id',
517
        'user_id',
518
        'skill_id',
519
        'acquired_skill_at',
520
        'assigned_by',
521
        'course_id',
522
        'session_id',
523
    ];
524
525
    /**
526
     * Constructor.
527
     */
528
    public function __construct()
529
    {
530
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
531
    }
532
533
    /**
534
     * @param array $skill_list
535
     *
536
     * @return array
537
     */
538
    public function getUserBySkills($skill_list)
539
    {
540
        $users = [];
541
        if (!empty($skill_list)) {
542
            $skill_list = array_map('intval', $skill_list);
543
            $skill_list = implode("', '", $skill_list);
544
545
            $sql = "SELECT user_id FROM {$this->table}
546
                    WHERE skill_id IN ('$skill_list') ";
547
548
            $result = Database::query($sql);
549
            $users = Database::store_result($result, 'ASSOC');
550
        }
551
552
        return $users;
553
    }
554
555
    /**
556
     * Get the achieved skills for the user.
557
     *
558
     * @param int $userId
559
     * @param int $courseId  Optional. The course id
560
     * @param int $sessionId Optional. The session id
561
     *
562
     * @return array The skill list. Otherwise return false
563
     */
564
    public function getUserSkills($userId, $courseId = 0, $sessionId = 0)
565
    {
566
        if (empty($userId)) {
567
            return [];
568
        }
569
570
        $courseId = intval($courseId);
571
        $sessionId = $sessionId ? intval($sessionId) : null;
572
        $whereConditions = [
573
            'user_id = ? ' => intval($userId),
574
        ];
575
576
        if ($courseId > 0) {
577
            $whereConditions['AND course_id = ? '] = $courseId;
578
            $whereConditions['AND session_id = ?'] = $sessionId;
579
        }
580
581
        $result = Database::select(
582
            'skill_id',
583
            $this->table,
584
            [
585
                'where' => $whereConditions,
586
            ],
587
            'all'
588
        );
589
590
        return $result;
591
    }
592
593
    /**
594
     * Get the relation data between user and skill.
595
     *
596
     * @param int $userId    The user id
597
     * @param int $skillId   The skill id
598
     * @param int $courseId  The course id
599
     * @param int $sessionId Optional. The session id
600
     *
601
     * @return array The relation data. Otherwise return false
602
     */
603
    public function getByUserAndSkill($userId, $skillId, $courseId, $sessionId = 0)
604
    {
605
        $where = [
606
            'user_id = ? AND skill_id = ? AND course_id = ? AND session_id = ?' => [
607
                intval($userId),
608
                intval($skillId),
609
                intval($courseId),
610
                $sessionId ? intval($sessionId) : null,
611
            ],
612
        ];
613
614
        return Database::select('*', $this->table, [
615
            'where' => $where,
616
        ], 'first');
617
    }
618
}
619
620
/**
621
 * Class Skill.
622
 */
623
class Skill extends Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
624
{
625
    public $columns = [
626
        'id',
627
        'name',
628
        'description',
629
        'access_url_id',
630
        'short_code',
631
        'icon',
632
        'criteria',
633
    ];
634
    public $required = ['name'];
635
636
    /** Array of colours by depth, for the coffee wheel. Each depth has 4 col */
637
    /*var $colours = array(
638
      0 => array('#f9f0ab', '#ecc099', '#e098b0', '#ebe378'),
639
      1 => array('#d5dda1', '#4a5072', '#8dae43', '#72659d'),
640
      2 => array('#b28647', '#2e6093', '#393e64', '#1e8323'),
641
      3 => array('#9f6652', '#9f6652', '#9f6652', '#9f6652'),
642
      4 => array('#af643c', '#af643c', '#af643c', '#af643c'),
643
      5 => array('#72659d', '#72659d', '#72659d', '#72659d'),
644
      6 => array('#8a6e9e', '#8a6e9e', '#8a6e9e', '#8a6e9e'),
645
      7 => array('#92538c', '#92538c', '#92538c', '#92538c'),
646
      8 => array('#2e6093', '#2e6093', '#2e6093', '#2e6093'),
647
      9 => array('#3a5988', '#3a5988', '#3a5988', '#3a5988'),
648
     10 => array('#393e64', '#393e64', '#393e64', '#393e64'),
649
    );*/
650
    public function __construct()
651
    {
652
        $this->table = Database::get_main_table(TABLE_MAIN_SKILL);
653
        $this->table_user = Database::get_main_table(TABLE_MAIN_USER);
0 ignored issues
show
Bug Best Practice introduced by
The property table_user does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
654
        $this->table_skill_rel_gradebook = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
0 ignored issues
show
Bug Best Practice introduced by
The property table_skill_rel_gradebook does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
655
        $this->table_skill_rel_user = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
0 ignored issues
show
Bug Best Practice introduced by
The property table_skill_rel_user does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
656
        $this->table_course = Database::get_main_table(TABLE_MAIN_COURSE);
0 ignored issues
show
Bug Best Practice introduced by
The property table_course does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
657
        $this->table_skill_rel_skill = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
0 ignored issues
show
Bug Best Practice introduced by
The property table_skill_rel_skill does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
658
        $this->table_gradebook = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
0 ignored issues
show
Bug Best Practice introduced by
The property table_gradebook does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
659
        $this->sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
0 ignored issues
show
Bug Best Practice introduced by
The property sessionTable does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
660
    }
661
662
    /**
663
     * Gets an element.
664
     *
665
     * @param int $id
666
     *
667
     * @return array|mixed
668
     */
669
    public function get($id)
670
    {
671
        $result = parent::get($id);
672
        if (empty($result)) {
673
            return [];
674
        }
675
676
        $path = api_get_path(WEB_UPLOAD_PATH).'badges/';
677
        if (!empty($result['icon'])) {
678
            $iconSmall = sprintf(
679
                "%s-small.png",
680
                sha1($result['name'])
681
            );
682
683
            $iconBig = sprintf(
684
                "%s.png",
685
                sha1($result['name'])
686
            );
687
688
            $iconMini = $path.$iconSmall;
689
            $iconSmall = $path.$iconSmall;
690
            $iconBig = $path.$iconBig;
691
        } else {
692
            $iconMini = Display::returnIconPath('badges-default.png', ICON_SIZE_MEDIUM);
693
            $iconSmall = Display::returnIconPath('badges-default.png', ICON_SIZE_BIG);
694
            $iconBig = Display::returnIconPath('badges-default.png', ICON_SIZE_HUGE);
695
        }
696
697
        $result['icon_mini'] = $iconMini;
698
        $result['icon_small'] = $iconSmall;
699
        $result['icon_big'] = $iconBig;
700
701
        $result['img_mini'] = Display::img($iconBig, $result['name'], ['width' => ICON_SIZE_MEDIUM]);
702
        $result['img_big'] = Display::img($iconBig, $result['name']);
703
        $result['img_small'] = Display::img($iconSmall, $result['name']);
704
        $result['name'] = self::translateName($result['name']);
705
        $result['short_code'] = self::translateCode($result['short_code']);
706
707
        return $result;
708
    }
709
710
    /**
711
     * @param array  $skills
712
     * @param string $imageSize     mini|small|big
713
     * @param bool   $addDivWrapper
714
     *
715
     * @return string
716
     */
717
    public function processSkillList($skills, $imageSize = '', $addDivWrapper = true)
718
    {
719
        if (empty($skills)) {
720
            return '';
721
        }
722
723
        if (empty($imageSize)) {
724
            $imageSize = 'img_small';
725
        } else {
726
            $imageSize = "img_$imageSize";
727
        }
728
729
        $html = '';
730
        if ($addDivWrapper) {
731
            $html = '<div class="scrollbar-inner badges-sidebar">';
732
        }
733
        $html .= '<ul class="list-unstyled list-badges">';
734
        foreach ($skills as $skill) {
735
            if (isset($skill['data'])) {
736
                $skill = $skill['data'];
737
            }
738
            $html .= '<li class="thumbnail">';
739
            $item = $skill[$imageSize];
740
            $item .= '<div class="caption">
741
                        <p class="text-center">'.$skill['name'].'</p>
742
                      </div>';
743
            if (isset($skill['url'])) {
744
                $html .= Display::url($item, $skill['url'], ['target' => '_blank']);
745
            } else {
746
                $html .= $item;
747
            }
748
            $html .= '</li>';
749
        }
750
        $html .= '</ul>';
751
752
        if ($addDivWrapper) {
753
            $html .= '</div>';
754
        }
755
756
        return $html;
757
    }
758
759
    /**
760
     * @param $skills
761
     * @param string $imageSize mini|small|big
762
     * @param string $style
763
     * @param bool   $showBadge
764
     * @param bool   $showTitle
765
     *
766
     * @return string
767
     */
768
    public function processSkillListSimple($skills, $imageSize = '', $style = '', $showBadge = true, $showTitle = true)
769
    {
770
        if (empty($skills)) {
771
            return '';
772
        }
773
774
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
775
776
        if (empty($imageSize)) {
777
            $imageSize = 'img_small';
778
        } else {
779
            $imageSize = "img_$imageSize";
780
        }
781
782
        $html = '';
783
        foreach ($skills as $skill) {
784
            if (isset($skill['data'])) {
785
                $skill = $skill['data'];
786
            }
787
788
            $item = '';
789
            if ($showBadge) {
790
                $item = $skill[$imageSize];
791
            }
792
793
            $name = $skill['name'];
794
            if (!empty($skill['short_code'])) {
795
                $name = $skill['short_code'];
796
            }
797
798
            if (!$isHierarchicalTable) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
799
                //$item .= '<br />';
800
            }
801
802
            if ($showTitle) {
803
                $item .= $name;
804
            }
805
806
            if (isset($skill['url'])) {
807
                $html .= Display::url($item, $skill['url'], ['target' => '_blank', 'style' => $style]);
808
            } else {
809
                $html .= Display::url($item, '#', ['target' => '_blank', 'style' => $style]);
810
            }
811
        }
812
813
        return $html;
814
    }
815
816
    /**
817
     * @param int $id
818
     *
819
     * @return array
820
     */
821
    public function getSkillInfo($id)
822
    {
823
        $skillRelSkill = new SkillRelSkill();
824
        $skillInfo = $this->get($id);
825
        if (!empty($skillInfo)) {
826
            $skillInfo['extra'] = $skillRelSkill->getSkillInfo($id);
827
            $skillInfo['gradebooks'] = self::getGradebooksBySkill($id);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::getGradebooksBySkill() 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

827
            /** @scrutinizer ignore-call */ 
828
            $skillInfo['gradebooks'] = self::getGradebooksBySkill($id);
Loading history...
828
        }
829
830
        return $skillInfo;
831
    }
832
833
    /**
834
     * @param array $skill_list
835
     *
836
     * @return array
837
     */
838
    public function getSkillsInfo($skill_list)
839
    {
840
        $skill_list = array_map('intval', $skill_list);
841
        $skill_list = implode("', '", $skill_list);
842
843
        $sql = "SELECT * FROM {$this->table}
844
                WHERE id IN ('$skill_list') ";
845
846
        $result = Database::query($sql);
847
        $skills = Database::store_result($result, 'ASSOC');
848
849
        foreach ($skills as &$skill) {
850
            if (!$skill['icon']) {
851
                continue;
852
            }
853
854
            $skill['icon_small'] = sprintf(
855
                "badges/%s-small.png",
856
                sha1($skill['name'])
857
            );
858
            $skill['name'] = self::translateName($skill['name']);
859
            $skill['short_code'] = self::translateCode($skill['short_code']);
860
        }
861
862
        return $skills;
863
    }
864
865
    /**
866
     * @param bool $load_user_data
867
     * @param bool $user_id
868
     * @param int  $id
869
     * @param int  $parent_id
870
     *
871
     * @return array
872
     */
873
    public function get_all(
874
        $load_user_data = false,
875
        $user_id = false,
876
        $id = null,
877
        $parent_id = null
878
    ) {
879
        $id_condition = '';
880
        if (!empty($id)) {
881
            $id = intval($id);
882
            $id_condition = " WHERE s.id = $id";
883
        }
884
885
        if (!empty($parent_id)) {
886
            $parent_id = intval($parent_id);
887
            if (empty($id_condition)) {
888
                $id_condition = " WHERE ss.parent_id = $parent_id";
889
            } else {
890
                $id_condition = " AND ss.parent_id = $parent_id";
891
            }
892
        }
893
894
        $sql = "SELECT
895
                    s.id,
896
                    s.name,
897
                    s.description,
898
                    ss.parent_id,
899
                    ss.relation_type,
900
                    s.icon,
901
                    s.short_code,
902
                    s.status
903
                FROM {$this->table} s
904
                INNER JOIN {$this->table_skill_rel_skill} ss
905
                ON (s.id = ss.skill_id) $id_condition
906
                ORDER BY ss.id, ss.parent_id";
907
908
        $result = Database::query($sql);
909
        $skills = [];
910
        $webPath = api_get_path(WEB_UPLOAD_PATH);
911
        if (Database::num_rows($result)) {
912
            while ($row = Database::fetch_array($result, 'ASSOC')) {
913
                $skillInfo = self::get($row['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::get() is not static, but was called statically. ( Ignorable by Annotation )

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

913
                /** @scrutinizer ignore-call */ 
914
                $skillInfo = self::get($row['id']);
Loading history...
914
915
                $row['img_mini'] = $skillInfo['img_mini'];
916
                $row['img_big'] = $skillInfo['img_big'];
917
                $row['img_small'] = $skillInfo['img_small'];
918
919
                $row['name'] = self::translateName($row['name']);
920
                $row['short_code'] = self::translateCode($row['short_code']);
921
                $skillRelSkill = new SkillRelSkill();
922
                $parents = $skillRelSkill->getSkillParents($row['id']);
923
                $row['level'] = count($parents) - 1;
924
                $row['gradebooks'] = self::getGradebooksBySkill($row['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::getGradebooksBySkill() 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

924
                /** @scrutinizer ignore-call */ 
925
                $row['gradebooks'] = self::getGradebooksBySkill($row['id']);
Loading history...
925
                $skills[$row['id']] = $row;
926
            }
927
        }
928
929
        // Load all children of the parent_id
930
        if (!empty($skills) && !empty($parent_id)) {
931
            foreach ($skills as $skill) {
932
                $children = self::get_all($load_user_data, $user_id, $id, $skill['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::get_all() is not static, but was called statically. ( Ignorable by Annotation )

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

932
                /** @scrutinizer ignore-call */ 
933
                $children = self::get_all($load_user_data, $user_id, $id, $skill['id']);
Loading history...
933
                if (!empty($children)) {
934
                    //$skills = array_merge($skills, $children);
935
                    $skills = $skills + $children;
936
                }
937
            }
938
        }
939
940
        return $skills;
941
    }
942
943
    /**
944
     * @param int $skill_id
945
     *
946
     * @return array|resource
947
     */
948
    public function getGradebooksBySkill($skill_id)
949
    {
950
        $skill_id = intval($skill_id);
951
        $sql = "SELECT g.* FROM {$this->table_gradebook} g
952
                INNER JOIN {$this->table_skill_rel_gradebook} sg
953
                ON g.id = sg.gradebook_id
954
                WHERE sg.skill_id = $skill_id";
955
        $result = Database::query($sql);
956
        $result = Database::store_result($result, 'ASSOC');
957
958
        return $result;
959
    }
960
961
    /**
962
     * Get one level children.
963
     *
964
     * @param int  $skill_id
965
     * @param bool $load_user_data
966
     *
967
     * @return array
968
     */
969
    public function getChildren($skill_id, $load_user_data = false)
970
    {
971
        $skillRelSkill = new SkillRelSkill();
972
        if ($load_user_data) {
973
            $user_id = api_get_user_id();
974
            $skills = $skillRelSkill->getChildren($skill_id, true, $user_id);
975
        } else {
976
            $skills = $skillRelSkill->getChildren($skill_id);
977
        }
978
979
        return $skills;
980
    }
981
982
    /**
983
     * Get all children of the current node (recursive).
984
     *
985
     * @param int $skillId
986
     *
987
     * @return array
988
     */
989
    public function getAllChildren($skillId)
990
    {
991
        $skillRelSkill = new SkillRelSkill();
992
        $children = $skillRelSkill->getChildren($skillId);
993
        foreach ($children as $child) {
994
            $subChildren = $this->getAllChildren($child['id']);
995
        }
996
997
        if (!empty($subChildren)) {
998
            $children = array_merge($children, $subChildren);
999
        }
1000
1001
        return $children;
1002
    }
1003
1004
    /**
1005
     * Gets all parents from from the wanted skill.
1006
     */
1007
    public function get_parents($skillId)
1008
    {
1009
        $skillRelSkill = new SkillRelSkill();
1010
        $skills = $skillRelSkill->getSkillParents($skillId, true);
1011
        foreach ($skills as &$skill) {
1012
            $skill['data'] = $this->get($skill['skill_id']);
1013
        }
1014
1015
        return $skills;
1016
    }
1017
1018
    /**
1019
     * All direct parents.
1020
     *
1021
     * @param int $skillId
1022
     *
1023
     * @return array
1024
     */
1025
    public function getDirectParents($skillId)
1026
    {
1027
        $skillRelSkill = new SkillRelSkill();
1028
        $skills = $skillRelSkill->getDirectParents($skillId, true);
1029
        if (!empty($skills)) {
1030
            foreach ($skills as &$skill) {
1031
                $skillData = $this->get($skill['skill_id']);
1032
                if (empty($skillData)) {
1033
                    continue;
1034
                }
1035
                $skill['data'] = $skillData;
1036
                $skill_info2 = $skillRelSkill->getSkillInfo($skill['skill_id']);
1037
                $parentId = isset($skill_info2['parent_id']) ? isset($skill_info2['parent_id']) : 0;
1038
                $skill['data']['parent_id'] = $parentId;
1039
            }
1040
1041
            return $skills;
1042
        }
1043
1044
        return [];
1045
    }
1046
1047
    /**
1048
     * Adds a new skill.
1049
     *
1050
     * @param array $params
1051
     *
1052
     * @return bool|null
1053
     */
1054
    public function add($params)
1055
    {
1056
        if (!isset($params['parent_id'])) {
1057
            $params['parent_id'] = 1;
1058
        }
1059
1060
        if (!is_array($params['parent_id'])) {
1061
            $params['parent_id'] = [$params['parent_id']];
1062
        }
1063
1064
        $skillRelSkill = new SkillRelSkill();
1065
        $skillRelGradebook = new SkillRelGradebook();
1066
1067
        // Saving name, description
1068
        $skill_id = $this->save($params);
1069
        if ($skill_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $skill_id of type integer|false 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...
1070
            //Saving skill_rel_skill (parent_id, relation_type)
1071
            foreach ($params['parent_id'] as $parent_id) {
1072
                $relation_exists = $skillRelSkill->relationExists($skill_id, $parent_id);
1073
                if (!$relation_exists) {
1074
                    $attributes = [
1075
                        'skill_id' => $skill_id,
1076
                        'parent_id' => $parent_id,
1077
                        'relation_type' => (isset($params['relation_type']) ? $params['relation_type'] : 0),
1078
                        //'level'         => $params['level'],
1079
                    ];
1080
                    $skillRelSkill->save($attributes);
1081
                }
1082
            }
1083
1084
            if (!empty($params['gradebook_id'])) {
1085
                foreach ($params['gradebook_id'] as $gradebook_id) {
1086
                    $attributes = [];
1087
                    $attributes['gradebook_id'] = $gradebook_id;
1088
                    $attributes['skill_id'] = $skill_id;
1089
                    $skillRelGradebook->save($attributes);
1090
                }
1091
            }
1092
1093
            return $skill_id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $skill_id returns the type integer which is incompatible with the documented return type null|boolean.
Loading history...
1094
        }
1095
1096
        return null;
1097
    }
1098
1099
    /**
1100
     * @param int $user_id
1101
     * @param int $gradebookId
1102
     * @param int $courseId
1103
     * @param int $sessionId
1104
     */
1105
    public function addSkillToUser(
1106
        $user_id,
1107
        $gradebookId,
1108
        $courseId = 0,
1109
        $sessionId = 0
1110
    ) {
1111
        $skill_gradebook = new SkillRelGradebook();
1112
        $skill_rel_user = new SkillRelUser();
1113
1114
        $skill_gradebooks = $skill_gradebook->get_all(
1115
            ['where' => ['gradebook_id = ?' => $gradebookId]]
1116
        );
1117
        if (!empty($skill_gradebooks)) {
1118
            foreach ($skill_gradebooks as $skill_gradebook) {
1119
                $user_has_skill = $this->userHasSkill(
1120
                    $user_id,
1121
                    $skill_gradebook['skill_id'],
1122
                    $courseId,
1123
                    $sessionId
1124
                );
1125
                if (!$user_has_skill) {
1126
                    $params = [
1127
                        'user_id' => $user_id,
1128
                        'skill_id' => $skill_gradebook['skill_id'],
1129
                        'acquired_skill_at' => api_get_utc_datetime(),
1130
                        'course_id' => intval($courseId),
1131
                        'session_id' => $sessionId ? intval($sessionId) : null,
1132
                    ];
1133
1134
                    $skill_rel_user->save($params);
1135
                }
1136
            }
1137
        }
1138
    }
1139
1140
    /* Deletes a skill */
1141
    public function delete($skill_id)
1142
    {
1143
        /*$params = array('skill_id' => $skill_id);
1144
1145
        $skillRelSkill     = new SkillRelSkill();
1146
        $skills = $skillRelSkill->get_all(array('where'=>array('skill_id = ?' =>$skill_id)));
1147
1148
        $skill_rel_profile     = new SkillRelProfile();
1149
        $skillRelGradebook = new SkillRelGradebook();
1150
        $skill_rel_user     = new SkillRelUser();
1151
1152
        $this->delete($skill_id);
1153
1154
        $skillRelGradebook->delete($params);*/
1155
    }
1156
1157
    /**
1158
     * @param array $params
1159
     */
1160
    public function edit($params)
1161
    {
1162
        if (!isset($params['parent_id'])) {
1163
            $params['parent_id'] = 1;
1164
        }
1165
1166
        $params['gradebook_id'] = isset($params['gradebook_id']) ? $params['gradebook_id'] : [];
1167
1168
        $skillRelSkill = new SkillRelSkill();
1169
        $skillRelGradebook = new SkillRelGradebook();
1170
1171
        // Saving name, description
1172
        $this->update($params);
1173
        $skillId = $params['id'];
1174
1175
        if ($skillId) {
1176
            // Saving skill_rel_skill (parent_id, relation_type)
1177
            if (!is_array($params['parent_id'])) {
1178
                $params['parent_id'] = [$params['parent_id']];
1179
            }
1180
1181
            // Cannot change parent of root
1182
            if ($skillId == 1) {
1183
                $params['parent_id'] = 0;
1184
            }
1185
1186
            foreach ($params['parent_id'] as $parent_id) {
1187
                $relation_exists = $skillRelSkill->relationExists($skillId, $parent_id);
1188
                if (!$relation_exists) {
1189
                    $attributes = [
1190
                        'skill_id' => $skillId,
1191
                        'parent_id' => $parent_id,
1192
                        'relation_type' => $params['relation_type'],
1193
                        //'level'         => $params['level'],
1194
                    ];
1195
                    $skillRelSkill->updateBySkill($attributes);
1196
                }
1197
            }
1198
1199
            $skillRelGradebook->updateGradeBookListBySkill(
1200
                $skillId,
1201
                $params['gradebook_id']
1202
            );
1203
1204
            return $skillId;
1205
        }
1206
1207
        return null;
1208
    }
1209
1210
    /**
1211
     * Get user's skills.
1212
     *
1213
     * @param int  $userId       User's id
1214
     * @param bool $getSkillData
1215
     * @param int  $courseId
1216
     * @param int  $sessionId
1217
     *
1218
     * @return array
1219
     */
1220
    public function getUserSkills($userId, $getSkillData = false, $courseId = 0, $sessionId = 0)
1221
    {
1222
        $userId = (int) $userId;
1223
        $courseId = (int) $courseId;
1224
        $sessionId = (int) $sessionId;
1225
1226
        $courseCondition = '';
1227
        if (!empty($courseId)) {
1228
            $courseCondition = " AND course_id = $courseId ";
1229
        }
1230
1231
        $sessionCondition = '';
1232
        if (!empty($sessionId)) {
1233
            $sessionCondition = " AND course_id = $sessionId ";
1234
        }
1235
1236
        $sql = 'SELECT DISTINCT
1237
                    s.id,
1238
                    s.name,
1239
                    s.icon,
1240
                    u.id as issue,
1241
                    u.acquired_skill_at,
1242
                    u.course_id
1243
                FROM '.$this->table_skill_rel_user.' u
1244
                INNER JOIN '.$this->table.' s
1245
                ON u.skill_id = s.id
1246
                WHERE
1247
                    user_id = '.$userId.' '.$sessionCondition.' '.$courseCondition;
1248
1249
        $result = Database::query($sql);
1250
        $skills = Database::store_result($result, 'ASSOC');
1251
        $skillList = [];
1252
        if (!empty($skills)) {
1253
            foreach ($skills as $skill) {
1254
                if ($getSkillData) {
1255
                    $skillData = $this->get($skill['id']);
1256
                    $skillData['url'] = api_get_path(WEB_PATH).'badge/'.$skill['issue'].'/user/'.$userId;
1257
                    $skillList[$skill['id']] = array_merge($skill, $skillData);
1258
                } else {
1259
                    $skillList[$skill['id']] = $skill['id'];
1260
                }
1261
            }
1262
        }
1263
1264
        return $skillList;
1265
    }
1266
1267
    /**
1268
     * @param Vertex $vertex
1269
     * @param array  $skills
1270
     * @param int    $level
1271
     *
1272
     * @return string
1273
     */
1274
    public function processVertex(Vertex $vertex, $skills = [], $level = 0)
1275
    {
1276
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
1277
        $subTable = '';
1278
        if ($vertex->getVerticesEdgeTo()->count() > 0) {
1279
            if ($isHierarchicalTable) {
1280
                $subTable .= '<ul>';
1281
            }
1282
            foreach ($vertex->getVerticesEdgeTo() as $subVertex) {
1283
                $data = $subVertex->getAttribute('graphviz.data');
1284
                $passed = in_array($data['id'], array_keys($skills));
1285
                $transparency = '';
1286
                if ($passed === false) {
1287
                    // @todo use css class
1288
                    $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1289
                }
1290
1291
                if ($isHierarchicalTable) {
1292
                    $label = $this->processSkillListSimple([$data], 'mini', $transparency);
1293
                    $subTable .= '<li>'.$label;
1294
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
1295
                    $subTable .= '</li>';
1296
                } else {
1297
                    $imageSize = 'mini';
1298
                    if ($level == 2) {
1299
                        $imageSize = 'small';
1300
                    }
1301
                    $showTitle = true;
1302
                    if ($level > 2) {
1303
                        $showTitle = false;
1304
                    }
1305
1306
                    $label = $this->processSkillListSimple([$data], $imageSize, $transparency, true, $showTitle);
1307
                    $subTable .= '<div class="thumbnail" style="float:left; margin-right:5px; ">';
1308
                    $subTable .= '<div style="'.$transparency.'">';
1309
1310
                    $subTable .= '<div style="text-align: center">';
1311
                    $subTable .= $label;
1312
                    $subTable .= '</div>';
1313
1314
                    $subTable .= '</div>';
1315
                    $subTable .= $this->processVertex($subVertex, $skills, $level + 1);
1316
                    $subTable .= '</div>';
1317
                }
1318
            }
1319
1320
            if ($isHierarchicalTable) {
1321
                $subTable .= '</ul>';
1322
            }
1323
        }
1324
1325
        return $subTable;
1326
    }
1327
1328
    /**
1329
     * @param int  $userId
1330
     * @param int  $courseId
1331
     * @param int  $sessionId
1332
     * @param bool $addTitle
1333
     *
1334
     * @return array
1335
     */
1336
    public function getUserSkillsTable($userId, $courseId = 0, $sessionId = 0, $addTitle = true)
1337
    {
1338
        $skills = $this->getUserSkills($userId, true, $courseId, $sessionId);
1339
1340
        $courseTempList = [];
1341
        $tableRows = [];
1342
        $skillParents = [];
1343
        foreach ($skills as $resultData) {
1344
            $parents = $this->get_parents($resultData['id']);
1345
            foreach ($parents as $parentData) {
1346
                $parentData['passed'] = in_array($parentData['id'], array_keys($skills));
1347
                if ($parentData['passed'] && isset($skills[$parentData['id']]['url'])) {
1348
                    $parentData['data']['url'] = $skills[$parentData['id']]['url'];
1349
                }
1350
                $skillParents[$resultData['id']][$parentData['id']] = $parentData;
1351
            }
1352
        }
1353
1354
        foreach ($skills as $resultData) {
1355
            $courseId = $resultData['course_id'];
1356
            if (!empty($courseId)) {
1357
                if (isset($courseTempList[$courseId])) {
1358
                    $courseInfo = $courseTempList[$courseId];
1359
                } else {
1360
                    $courseInfo = api_get_course_info_by_id($courseId);
1361
                    $courseTempList[$courseId] = $courseInfo;
1362
                }
1363
            }
1364
1365
            $tableRow = [
1366
                'skill_badge' => $resultData['img_mini'],
1367
                'skill_name' => self::translateName($resultData['name']),
1368
                'short_code' => $resultData['short_code'],
1369
                'achieved_at' => api_get_local_time($resultData['acquired_skill_at']),
1370
                'course_image' => '',
1371
                'course_name' => '',
1372
            ];
1373
1374
            if (!empty($courseInfo)) {
1375
                $tableRow['course_image'] = $courseInfo['course_image_source'];
1376
                $tableRow['course_name'] = $courseInfo['title'];
1377
            }
1378
            $tableRows[] = $tableRow;
1379
        }
1380
1381
        $isHierarchicalTable = api_get_configuration_value('table_of_hierarchical_skill_presentation');
1382
        $allowLevels = api_get_configuration_value('skill_levels_names');
1383
1384
        $tableResult = '<div id="skillList">';
1385
        if ($isHierarchicalTable) {
1386
            $tableResult = '<div class="table-responsive">';
1387
        }
1388
1389
        if ($addTitle) {
1390
            $tableResult .= '
1391
                    <table class="table">
1392
                        <thead>
1393
                            <tr>
1394
                                <th>'.get_lang('AchievedSkills').'</th>
1395
                            </tr>
1396
                        </thead>
1397
                        <tbody>
1398
                        <tr><td>';
1399
        }
1400
1401
        if (!empty($skillParents)) {
1402
            if (empty($allowLevels)) {
1403
                $tableResult .= $this->processSkillListSimple($skills);
1404
            } else {
1405
                $graph = new Graph();
1406
                $graph->setAttribute('graphviz.graph.rankdir', 'LR');
1407
                foreach ($skillParents as $skillId => $parentList) {
1408
                    $old = null;
1409
                    foreach ($parentList as $parent) {
1410
                        if ($graph->hasVertex($parent['id'])) {
1411
                            $current = $graph->getVertex($parent['id']);
1412
                        } else {
1413
                            $current = $graph->createVertex($parent['id']);
1414
                            $current->setAttribute('graphviz.data', $parent['data']);
1415
                        }
1416
1417
                        if (!empty($old)) {
1418
                            if ($graph->hasVertex($old['id'])) {
1419
                                $nextVertex = $graph->getVertex($old['id']);
1420
                            } else {
1421
                                $nextVertex = $graph->createVertex($old['id']);
1422
                                $nextVertex->setAttribute('graphviz.data', $old['data']);
1423
                            }
1424
1425
                            if (!$nextVertex->hasEdgeTo($current)) {
1426
                                $nextVertex->createEdgeTo($current);
1427
                            }
1428
                        }
1429
                        $old = $parent;
1430
                    }
1431
                }
1432
1433
                if ($isHierarchicalTable) {
1434
                    $table = '<table class ="table table-bordered">';
1435
                    // Getting "root" vertex
1436
                    $root = $graph->getVertex(1);
1437
                    $table .= '<tr>';
1438
                    /** @var Vertex $vertex */
1439
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
1440
                        $data = $vertex->getAttribute('graphviz.data');
1441
1442
                        $passed = in_array($data['id'], array_keys($skills));
1443
                        $transparency = '';
1444
                        if ($passed === false) {
1445
                            // @todo use a css class
1446
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1447
                        }
1448
1449
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency);
1450
                        $table .= '<td >';
1451
1452
                        $table .= '<div class="skills_chart"> <ul><li>'.$label;
1453
                        $table .= $this->processVertex($vertex, $skills);
1454
                        $table .= '</ul></li></div>';
1455
                        $table .= '</td>';
1456
                    }
1457
                    $table .= '</tr></table>';
1458
                } else {
1459
                    // Getting "root" vertex
1460
                    $root = $graph->getVertex(1);
1461
                    $table = '';
1462
                    /** @var Vertex $vertex */
1463
                    foreach ($root->getVerticesEdgeTo() as $vertex) {
1464
                        $data = $vertex->getAttribute('graphviz.data');
1465
1466
                        $passed = in_array($data['id'], array_keys($skills));
1467
                        $transparency = '';
1468
                        if ($passed === false) {
1469
                            // @todo use a css class
1470
                            $transparency = 'opacity: 0.4; filter: alpha(opacity=40);';
1471
                        }
1472
1473
                        $label = $this->processSkillListSimple([$data], 'mini', $transparency, false);
1474
1475
                        $skillTable = $this->processVertex($vertex, $skills, 2);
1476
                        $table .= "<h3>$label</h3>";
1477
1478
                        if (!empty($skillTable)) {
1479
                            $table .= '<table class ="table table-bordered">';
1480
                            $table .= '<tr>';
1481
                            $table .= '<td>';
1482
                            $table .= '<div>';
1483
                            $table .= $skillTable;
1484
                            $table .= '</div>';
1485
                            $table .= '</td>';
1486
                            $table .= '</tr></table>';
1487
                        }
1488
                    }
1489
                }
1490
1491
                $tableResult .= $table;
1492
            }
1493
        } else {
1494
            $tableResult .= get_lang('WithoutAchievedSkills');
1495
        }
1496
1497
        if ($addTitle) {
1498
            $tableResult .= '</td>
1499
                            </tr>
1500
                        </tbody>
1501
                    </table>
1502
                ';
1503
        }
1504
        $tableResult .= '</div>';
1505
1506
        return [
1507
            'skills' => $tableRows,
1508
            'table' => $tableResult,
1509
        ];
1510
    }
1511
1512
    /**
1513
     * @param int  $user_id
1514
     * @param int  $skill_id
1515
     * @param bool $return_flat_array
1516
     * @param bool $add_root
1517
     *
1518
     * @return array|null
1519
     */
1520
    public function getSkillsTree(
1521
        $user_id = null,
1522
        $skill_id = null,
1523
        $return_flat_array = false,
1524
        $add_root = false
1525
    ) {
1526
        if ($skill_id == 1) {
1527
            $skill_id = 0;
1528
        }
1529
        if (isset($user_id) && !empty($user_id)) {
1530
            $skills = $this->get_all(true, $user_id, null, $skill_id);
1531
        } else {
1532
            $skills = $this->get_all(false, false, null, $skill_id);
1533
        }
1534
1535
        $original_skill = $this->list = $skills;
0 ignored issues
show
Bug Best Practice introduced by
The property list does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1536
1537
        // Show 1 item
1538
        if (!empty($skill_id)) {
1539
            if ($add_root) {
1540
                if (!empty($skill_id)) {
1541
                    // Default root node
1542
                    $skills[1] = [
1543
                        'id' => '1',
1544
                        'name' => get_lang('Root'),
1545
                        'parent_id' => '0',
1546
                        'status' => 1,
1547
                    ];
1548
                    $skillInfo = $this->getSkillInfo($skill_id);
1549
1550
                    // 2nd node
1551
                    $skills[$skill_id] = $skillInfo;
1552
                    // Uncomment code below to hide the searched skill
1553
                    $skills[$skill_id]['data']['parent_id'] = $skillInfo['extra']['parent_id'];
1554
                    $skills[$skill_id]['parent_id'] = 1;
1555
                }
1556
            }
1557
        }
1558
1559
        $refs = [];
1560
        $skills_tree = null;
1561
1562
        // Create references for all nodes
1563
        $flat_array = [];
1564
        $family = [];
1565
        if (!empty($skills)) {
1566
            foreach ($skills as &$skill) {
1567
                if ($skill['parent_id'] == 0) {
1568
                    $skill['parent_id'] = 'root';
1569
                }
1570
1571
                // because except main keys (id, name, children) others keys
1572
                // are not saved while in the space tree
1573
                $skill['data'] = ['parent_id' => $skill['parent_id']];
1574
1575
                // If a short code was defined, send the short code to replace
1576
                // skill name (to shorten the text in the wheel)
1577
                if (!empty($skill['short_code']) &&
1578
                    api_get_setting('show_full_skill_name_on_skill_wheel') === 'false'
1579
                ) {
1580
                    $skill['data']['short_code'] = $skill['short_code'];
1581
                }
1582
1583
                $skill['data']['name'] = $skill['name'];
1584
                $skill['data']['status'] = $skill['status'];
1585
1586
                // In order to paint all members of a family with the same color
1587
                if (empty($skill_id)) {
1588
                    if ($skill['parent_id'] == 1) {
1589
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1590
                    }
1591
                } else {
1592
                    if ($skill['parent_id'] == $skill_id) {
1593
                        $family[$skill['id']] = $this->getAllChildren($skill['id']);
1594
                    }
1595
                    /*if ($skill_id == $skill['id']) {
1596
                        $skill['parent_id'] = 1;
1597
                    }*/
1598
                }
1599
1600
                if (!isset($skill['data']['real_parent_id'])) {
1601
                    $skill['data']['real_parent_id'] = $skill['parent_id'];
1602
                }
1603
1604
                // User achieved the skill (depends in the gradebook with certification)
1605
                $skill['data']['achieved'] = false;
1606
                if ($user_id) {
1607
                    $skill['data']['achieved'] = $this->userHasSkill(
1608
                        $user_id,
1609
                        $skill['id']
1610
                    );
1611
                }
1612
1613
                // Check if the skill has related gradebooks
1614
                $skill['data']['skill_has_gradebook'] = false;
1615
                if (isset($skill['gradebooks']) && !empty($skill['gradebooks'])) {
1616
                    $skill['data']['skill_has_gradebook'] = true;
1617
                }
1618
                $refs[$skill['id']] = &$skill;
1619
                $flat_array[$skill['id']] = &$skill;
1620
            }
1621
1622
            // Checking family value
1623
1624
            $family_id = 1;
1625
            $new_family_array = [];
1626
            foreach ($family as $main_family_id => $family_items) {
1627
                if (!empty($family_items)) {
1628
                    foreach ($family_items as $item) {
1629
                        $new_family_array[$item['id']] = $family_id;
1630
                    }
1631
                }
1632
                $new_family_array[$main_family_id] = $family_id;
1633
                $family_id++;
1634
            }
1635
1636
            if (empty($original_skill)) {
1637
                $refs['root']['children'][0] = $skills[1];
1638
                $skills[$skill_id]['data']['family_id'] = 1;
1639
                $refs['root']['children'][0]['children'][0] = $skills[$skill_id];
1640
                $flat_array[$skill_id] = $skills[$skill_id];
1641
            } else {
1642
                // Moving node to the children index of their parents
1643
                foreach ($skills as $my_skill_id => &$skill) {
1644
                    if (isset($new_family_array[$skill['id']])) {
1645
                        $skill['data']['family_id'] = $new_family_array[$skill['id']];
1646
                    }
1647
                    $refs[$skill['parent_id']]['children'][] = &$skill;
1648
                    $flat_array[$my_skill_id] = $skill;
1649
                }
1650
            }
1651
1652
            $skills_tree = [
1653
                'name' => get_lang('SkillRootName'),
1654
                'id' => 'root',
1655
                'children' => $refs['root']['children'],
1656
                'data' => [],
1657
            ];
1658
        }
1659
1660
        if ($return_flat_array) {
1661
            return $flat_array;
1662
        }
1663
        unset($skills);
1664
1665
        return $skills_tree;
1666
    }
1667
1668
    /**
1669
     * Get skills tree as a simplified JSON structure.
1670
     *
1671
     * @param int user id
1672
     * @param int skill id
1673
     * @param bool return a flat array or not
1674
     * @param int depth of the skills
1675
     *
1676
     * @return string json
1677
     */
1678
    public function getSkillsTreeToJson(
1679
        $user_id = null,
1680
        $skill_id = null,
1681
        $return_flat_array = false,
1682
        $main_depth = 2
1683
    ) {
1684
        $tree = $this->getSkillsTree(
1685
            $user_id,
1686
            $skill_id,
1687
            $return_flat_array,
1688
            true
1689
        );
1690
        $simple_tree = [];
1691
        if (!empty($tree['children'])) {
1692
            foreach ($tree['children'] as $element) {
1693
                $children = [];
1694
                if (isset($element['children'])) {
1695
                    $children = $this->getSkillToJson($element['children'], 1, $main_depth);
1696
                }
1697
                $simple_tree[] = [
1698
                    'name' => $element['name'],
1699
                    'children' => $children,
1700
                ];
1701
            }
1702
        }
1703
1704
        return json_encode($simple_tree[0]['children']);
1705
    }
1706
1707
    /**
1708
     * Get JSON element.
1709
     *
1710
     * @param array $subtree
1711
     * @param int   $depth
1712
     * @param int   $max_depth
1713
     *
1714
     * @return array|null
1715
     */
1716
    public function getSkillToJson($subtree, $depth = 1, $max_depth = 2)
1717
    {
1718
        $simple_sub_tree = [];
1719
        if (is_array($subtree)) {
1720
            $counter = 1;
1721
            foreach ($subtree as $elem) {
1722
                $tmp = [];
1723
                $tmp['name'] = $elem['name'];
1724
                $tmp['id'] = $elem['id'];
1725
                $tmp['isSearched'] = self::isSearched($elem['id']);
1726
1727
                if (isset($elem['children']) && is_array($elem['children'])) {
1728
                    $tmp['children'] = $this->getSkillToJson(
1729
                        $elem['children'],
1730
                        $depth + 1,
1731
                        $max_depth
1732
                    );
1733
                } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
1734
                    //$tmp['colour'] = $this->colours[$depth][rand(0,3)];
1735
                }
1736
1737
                if ($depth > $max_depth) {
1738
                    continue;
1739
                }
1740
1741
                $tmp['depth'] = $depth;
1742
                $tmp['counter'] = $counter;
1743
                $counter++;
1744
1745
                if (isset($elem['data']) && is_array($elem['data'])) {
1746
                    foreach ($elem['data'] as $key => $item) {
1747
                        $tmp[$key] = $item;
1748
                    }
1749
                }
1750
                $simple_sub_tree[] = $tmp;
1751
            }
1752
1753
            return $simple_sub_tree;
1754
        }
1755
1756
        return null;
1757
    }
1758
1759
    /**
1760
     * @param int $user_id
1761
     *
1762
     * @return bool
1763
     */
1764
    public function getUserSkillRanking($user_id)
1765
    {
1766
        $user_id = intval($user_id);
1767
        $sql = "SELECT count(skill_id) count FROM {$this->table} s
1768
                INNER JOIN {$this->table_skill_rel_user} su
1769
                ON (s.id = su.skill_id)
1770
                WHERE user_id = $user_id";
1771
        $result = Database::query($sql);
1772
        if (Database::num_rows($result)) {
1773
            $result = Database::fetch_row($result);
1774
1775
            return $result[0];
1776
        }
1777
1778
        return false;
1779
    }
1780
1781
    /**
1782
     * @param $start
1783
     * @param $limit
1784
     * @param $sidx
1785
     * @param $sord
1786
     * @param $where_condition
1787
     *
1788
     * @return array
1789
     */
1790
    public function getUserListSkillRanking(
1791
        $start,
1792
        $limit,
1793
        $sidx,
0 ignored issues
show
Unused Code introduced by
The parameter $sidx is not used and could be removed. ( Ignorable by Annotation )

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

1793
        /** @scrutinizer ignore-unused */ $sidx,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1794
        $sord,
0 ignored issues
show
Unused Code introduced by
The parameter $sord is not used and could be removed. ( Ignorable by Annotation )

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

1794
        /** @scrutinizer ignore-unused */ $sord,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1795
        $where_condition
1796
    ) {
1797
        $start = intval($start);
1798
        $limit = intval($limit);
1799
        /*  ORDER BY $sidx $sord */
1800
        $sql = "SELECT *, @rownum:=@rownum+1 rank FROM (
1801
                    SELECT u.user_id, firstname, lastname, count(username) skills_acquired
1802
                    FROM {$this->table} s INNER JOIN {$this->table_skill_rel_user} su ON (s.id = su.skill_id)
1803
                    INNER JOIN {$this->table_user} u ON u.user_id = su.user_id, (SELECT @rownum:=0) r
1804
                    WHERE 1=1 $where_condition
1805
                    GROUP BY username
1806
                    ORDER BY skills_acquired desc
1807
                    LIMIT $start , $limit)  AS T1, (SELECT @rownum:=0) r";
1808
        $result = Database::query($sql);
1809
        if (Database::num_rows($result)) {
1810
            return Database::store_result($result, 'ASSOC');
1811
        }
1812
1813
        return [];
1814
    }
1815
1816
    /**
1817
     * @return int
1818
     */
1819
    public function getUserListSkillRankingCount()
1820
    {
1821
        $sql = "SELECT count(*) FROM (
1822
                    SELECT count(distinct 1)
1823
                    FROM {$this->table} s
1824
                    INNER JOIN {$this->table_skill_rel_user} su
1825
                    ON (s.id = su.skill_id)
1826
                    INNER JOIN {$this->table_user} u
1827
                    ON u.user_id = su.user_id
1828
                    GROUP BY username
1829
                 ) as T1";
1830
        $result = Database::query($sql);
1831
        if (Database::num_rows($result)) {
1832
            $result = Database::fetch_row($result);
1833
1834
            return $result[0];
1835
        }
1836
1837
        return 0;
1838
    }
1839
1840
    /**
1841
     * @param string $courseCode
1842
     *
1843
     * @return int
1844
     */
1845
    public function getCountSkillsByCourse($courseCode)
1846
    {
1847
        $courseCode = Database::escape_string($courseCode);
1848
        $sql = "SELECT count(skill_id) as count
1849
                FROM {$this->table_gradebook} g
1850
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1851
                ON g.id = sg.gradebook_id
1852
                WHERE course_code = '$courseCode'";
1853
1854
        $result = Database::query($sql);
1855
        if (Database::num_rows($result)) {
1856
            $result = Database::fetch_row($result);
1857
1858
            return $result[0];
1859
        }
1860
1861
        return 0;
1862
    }
1863
1864
    /**
1865
     * @param int $skillId
1866
     *
1867
     * @return array
1868
     */
1869
    public function getCoursesBySkill($skillId)
1870
    {
1871
        $skillId = intval($skillId);
1872
        $sql = "SELECT c.title, c.code
1873
                FROM {$this->table_gradebook} g
1874
                INNER JOIN {$this->table_skill_rel_gradebook} sg
1875
                ON g.id = sg.gradebook_id
1876
                INNER JOIN {$this->table_course} c
1877
                ON c.code = g.course_code
1878
                WHERE sg.skill_id = $skillId
1879
                AND (g.session_id IS NULL OR g.session_id = 0)";
1880
        $result = Database::query($sql);
1881
1882
        return Database::store_result($result, 'ASSOC');
1883
    }
1884
1885
    /**
1886
     * Check if the user has the skill.
1887
     *
1888
     * @param int $userId    The user id
1889
     * @param int $skillId   The skill id
1890
     * @param int $courseId  Optional. The course id
1891
     * @param int $sessionId Optional. The session id
1892
     *
1893
     * @return bool Whether the user has the skill return true. Otherwise return false
1894
     */
1895
    public function userHasSkill($userId, $skillId, $courseId = 0, $sessionId = 0)
1896
    {
1897
        $courseId = intval($courseId);
1898
        $sessionId = intval($sessionId);
1899
1900
        $whereConditions = [
1901
            'user_id = ? ' => intval($userId),
1902
            'AND skill_id = ? ' => intval($skillId),
1903
        ];
1904
1905
        if ($courseId > 0) {
1906
            $whereConditions['AND course_id = ? '] = $courseId;
1907
            $whereConditions['AND session_id = ? '] = $sessionId ? $sessionId : null;
1908
        }
1909
1910
        $result = Database::select(
1911
            'COUNT(1) AS qty',
1912
            $this->table_skill_rel_user,
1913
            [
1914
                'where' => $whereConditions,
1915
            ],
1916
            'first'
1917
        );
1918
1919
        if ($result != false) {
1920
            if ($result['qty'] > 0) {
1921
                return true;
1922
            }
1923
        }
1924
1925
        return false;
1926
    }
1927
1928
    /**
1929
     * Check if a skill is searched.
1930
     *
1931
     * @param int $id The skill id
1932
     *
1933
     * @return bool Whether el skill is searched return true. Otherwise return false
1934
     */
1935
    public static function isSearched($id)
1936
    {
1937
        $id = intval($id);
1938
1939
        if (empty($id)) {
1940
            return false;
1941
        }
1942
1943
        $skillRelProfileTable = Database::get_main_table(TABLE_MAIN_SKILL_REL_PROFILE);
1944
1945
        $result = Database::select(
1946
            'COUNT( DISTINCT `skill_id`) AS qty',
1947
            $skillRelProfileTable,
1948
            [
1949
                'where' => [
1950
                    'skill_id = ?' => $id,
1951
                ],
1952
            ],
1953
            'first'
1954
        );
1955
1956
        if ($result === false) {
1957
            return false;
1958
        }
1959
1960
        if ($result['qty'] > 0) {
1961
            return true;
1962
        }
1963
1964
        return false;
1965
    }
1966
1967
    /**
1968
     * Get the achieved skills by course.
1969
     *
1970
     * @param int $courseId The course id
1971
     *
1972
     * @return array The skills list
1973
     */
1974
    public function listAchievedByCourse($courseId)
1975
    {
1976
        $courseId = intval($courseId);
1977
1978
        if ($courseId == 0) {
1979
            return [];
1980
        }
1981
1982
        $list = [];
1983
1984
        $sql = "SELECT
1985
                    course.id c_id,
1986
                    course.title c_name,
1987
                    course.directory c_directory,
1988
                    user.user_id,
1989
                    user.lastname,
1990
                    user.firstname,
1991
                    user.username,
1992
                    skill.id skill_id,
1993
                    skill.name skill_name,
1994
                    sru.acquired_skill_at
1995
                FROM {$this->table_skill_rel_user} AS sru
1996
                INNER JOIN {$this->table_course}
1997
                ON sru.course_id = course.id
1998
                INNER JOIN {$this->table_user}
1999
                ON sru.user_id = user.user_id
2000
                INNER JOIN {$this->table}
2001
                ON sru.skill_id = skill.id
2002
                WHERE course.id = $courseId";
2003
2004
        $result = Database::query($sql);
2005
2006
        while ($row = Database::fetch_assoc($result)) {
2007
            $row['skill_name'] = self::translateName($row['skill_name']);
2008
            $list[] = $row;
2009
        }
2010
2011
        return $list;
2012
    }
2013
2014
    /**
2015
     * Get the users list who achieved a skill.
2016
     *
2017
     * @param int $skillId The skill id
2018
     *
2019
     * @return array The users list
2020
     */
2021
    public function listUsersWhoAchieved($skillId)
2022
    {
2023
        $skillId = intval($skillId);
2024
2025
        if ($skillId == 0) {
2026
            return [];
2027
        }
2028
2029
        $list = [];
2030
        $sql = "SELECT
2031
                    course.id c_id,
2032
                    course.title c_name,
2033
                    course.directory c_directory,
2034
                    user.user_id,
2035
                    user.lastname,
2036
                    user.firstname,
2037
                    user.username,
2038
                    skill.id skill_id,
2039
                    skill.name skill_name,
2040
                    sru.acquired_skill_at
2041
                FROM {$this->table_skill_rel_user} AS sru
2042
                INNER JOIN {$this->table_course}
2043
                ON sru.course_id = course.id
2044
                INNER JOIN {$this->table_user}
2045
                ON sru.user_id = user.user_id
2046
                INNER JOIN {$this->table}
2047
                ON sru.skill_id = skill.id
2048
                WHERE skill.id = $skillId ";
2049
2050
        $result = Database::query($sql);
2051
        while ($row = Database::fetch_assoc($result)) {
2052
            $row['skill_name'] = self::translateName($row['skill_name']);
2053
            $list[] = $row;
2054
        }
2055
2056
        return $list;
2057
    }
2058
2059
    /**
2060
     * Get the session list where the user can achieve a skill.
2061
     *
2062
     * @param int $skillId The skill id
2063
     *
2064
     * @return array
2065
     */
2066
    public function getSessionsBySkill($skillId)
2067
    {
2068
        $skillId = intval($skillId);
2069
2070
        $sql = "SELECT s.id, s.name
2071
                FROM {$this->table_gradebook} g
2072
                INNER JOIN {$this->table_skill_rel_gradebook} sg
2073
                ON g.id = sg.gradebook_id
2074
                INNER JOIN {$this->sessionTable} s
2075
                ON g.session_id = s.id
2076
                WHERE sg.skill_id = $skillId
2077
                AND g.session_id > 0";
2078
2079
        $result = Database::query($sql);
2080
2081
        return Database::store_result($result, 'ASSOC');
2082
    }
2083
2084
    /**
2085
     * Check if the $fromUser can comment the $toUser skill issue.
2086
     *
2087
     * @param User $fromUser
2088
     * @param User $toUser
2089
     *
2090
     * @return bool
2091
     */
2092
    public static function userCanAddFeedbackToUser($fromUser, $toUser)
2093
    {
2094
        if (api_is_platform_admin()) {
2095
            return true;
2096
        }
2097
2098
        $entityManager = Database::getManager();
2099
        /** @var UserRepository $userRepo */
2100
        $userRepo = $entityManager->getRepository('ChamiloUserBundle:User');
2101
        $fromUserStatus = $fromUser->getStatus();
2102
2103
        switch ($fromUserStatus) {
2104
            case SESSIONADMIN:
2105
                if (api_get_setting('allow_session_admins_to_manage_all_sessions') === 'true') {
2106
                    if ($toUser->getCreatorId() === $fromUser->getId()) {
2107
                        return true;
2108
                    }
2109
                }
2110
2111
                $sessionAdmins = $userRepo->getSessionAdmins($toUser);
2112
2113
                foreach ($sessionAdmins as $sessionAdmin) {
2114
                    if ($sessionAdmin->getId() !== $fromUser->getId()) {
2115
                        continue;
2116
                    }
2117
2118
                    return true;
2119
                }
2120
                break;
2121
            case STUDENT_BOSS:
2122
                $studentBosses = $userRepo->getStudentBosses($toUser);
2123
                foreach ($studentBosses as $studentBoss) {
2124
                    if ($studentBoss->getId() !== $fromUser->getId()) {
2125
                        continue;
2126
                    }
2127
2128
                    return true;
2129
                }
2130
                break;
2131
            case DRH:
2132
                return UserManager::is_user_followed_by_drh(
2133
                    $toUser->getId(),
2134
                    $fromUser->getId()
2135
                );
2136
        }
2137
2138
        return false;
2139
    }
2140
2141
    /**
2142
     * If $studentId is set then check if current user has the right to see
2143
     * the page.
2144
     *
2145
     * @param int  $studentId check if current user has access to see $studentId
2146
     * @param bool $blockPage raise a api_not_allowed()
2147
     *
2148
     * @return bool
2149
     */
2150
    public static function isAllowed($studentId = 0, $blockPage = true)
2151
    {
2152
        if (self::isToolAvailable()) {
2153
            if (api_is_platform_admin()) {
2154
                return true;
2155
            }
2156
2157
            if (!empty($studentId)) {
2158
                $currentUserId = api_get_user_id();
2159
                if ((int) $currentUserId === (int) $studentId) {
2160
                    return true;
2161
                }
2162
2163
                $haveAccess = self::hasAccessToUserSkill(
2164
                    $currentUserId,
2165
                    $studentId
2166
                );
2167
2168
                if ($haveAccess) {
2169
                    return true;
2170
                }
2171
            }
2172
        }
2173
2174
        if ($blockPage) {
2175
            api_not_allowed(true);
2176
        }
2177
2178
        return false;
2179
    }
2180
2181
    /**
2182
     * @return bool
2183
     */
2184
    public static function isToolAvailable()
2185
    {
2186
        $allowTool = api_get_setting('allow_skills_tool');
2187
2188
        if ($allowTool === 'true') {
2189
            return true;
2190
        }
2191
2192
        return false;
2193
    }
2194
2195
    /**
2196
     * @param $currentUserId
2197
     * @param $studentId
2198
     *
2199
     * @return bool
2200
     */
2201
    public static function hasAccessToUserSkill($currentUserId, $studentId)
2202
    {
2203
        if (self::isToolAvailable()) {
2204
            if (api_is_platform_admin()) {
2205
                return true;
2206
            }
2207
2208
            if (api_is_student_boss()) {
2209
                $isBoss = UserManager::userIsBossOfStudent($currentUserId, $studentId);
2210
                if ($isBoss) {
2211
                    return true;
2212
                }
2213
            }
2214
2215
            $allow = api_get_configuration_value('allow_private_skills');
2216
            if ($allow === true) {
2217
                if (api_is_teacher()) {
2218
                    return UserManager::isTeacherOfStudent(
2219
                        $currentUserId,
2220
                        $studentId
2221
                    );
2222
                }
2223
2224
                if (api_is_drh()) {
2225
                    return UserManager::is_user_followed_by_drh(
2226
                        $studentId,
2227
                        $currentUserId
2228
                    );
2229
                }
2230
            }
2231
        }
2232
2233
        return false;
2234
    }
2235
2236
    /**
2237
     * Get skills.
2238
     *
2239
     * @param int $userId
2240
     * @param int level
2241
     *
2242
     * @return array
2243
     */
2244
    public function getStudentSkills($userId, $level = 0)
2245
    {
2246
        $sql = "SELECT s.id, s.name, sru.acquired_skill_at
2247
                FROM {$this->table} s
2248
                INNER JOIN {$this->table_skill_rel_user} sru
2249
                ON s.id = sru.skill_id
2250
                WHERE sru.user_id = $userId";
2251
2252
        $result = Database::query($sql);
2253
2254
        $skills = [];
2255
        foreach ($result as $item) {
2256
            if (empty($level)) {
2257
                $skills[] = [
2258
                    'name' => self::translateName($item['name']),
2259
                    'acquired_skill_at' => $item['acquired_skill_at'],
2260
                ];
2261
            } else {
2262
                $parents = self::get_parents($item['id']);
0 ignored issues
show
Bug Best Practice introduced by
The method Skill::get_parents() is not static, but was called statically. ( Ignorable by Annotation )

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

2262
                /** @scrutinizer ignore-call */ 
2263
                $parents = self::get_parents($item['id']);
Loading history...
2263
                // +2 because it takes into account the root
2264
                if (count($parents) == $level + 1) {
2265
                    $skills[] = [
2266
                        'name' => self::translateName($item['name']),
2267
                        'acquired_skill_at' => $item['acquired_skill_at'],
2268
                    ];
2269
                }
2270
            }
2271
        }
2272
2273
        return $skills;
2274
    }
2275
2276
    /**
2277
     * @param string $name
2278
     *
2279
     * @return string
2280
     */
2281
    public static function translateName($name)
2282
    {
2283
        $variable = ChamiloApi::getLanguageVar($name, 'Skill');
2284
2285
        return isset($GLOBALS[$variable]) ? $GLOBALS[$variable] : $name;
2286
    }
2287
2288
    /**
2289
     * @param string $code
2290
     *
2291
     * @return mixed|string
2292
     */
2293
    public static function translateCode($code)
2294
    {
2295
        if (empty($code)) {
2296
            return '';
2297
        }
2298
2299
        $variable = ChamiloApi::getLanguageVar($code, 'SkillCode');
2300
2301
        return isset($GLOBALS[$variable]) ? $GLOBALS[$variable] : $code;
2302
    }
2303
2304
    /**
2305
     * @param FormValidator $form
2306
     * @param array         $skillInfo
2307
     *
2308
     * @return array
2309
     */
2310
    public function setForm(FormValidator &$form, $skillInfo = [])
2311
    {
2312
        $allSkills = $this->get_all();
2313
        $objGradebook = new Gradebook();
2314
2315
        $isAlreadyRootSkill = false;
2316
        foreach ($allSkills as $checkedSkill) {
2317
            if (intval($checkedSkill['parent_id']) > 0) {
2318
                $isAlreadyRootSkill = true;
2319
                break;
2320
            }
2321
        }
2322
2323
        $skillList = $isAlreadyRootSkill ? [] : [0 => get_lang('None')];
2324
2325
        foreach ($allSkills as $skill) {
2326
            if (isset($skillInfo['id']) && $skill['id'] == $skillInfo['id']) {
2327
                continue;
2328
            }
2329
2330
            $skillList[$skill['id']] = $skill['name'];
2331
        }
2332
2333
        $allGradeBooks = $objGradebook->find('all');
2334
2335
        // This procedure is for check if there is already a Skill with no Parent (Root by default)
2336
        $gradeBookList = [];
2337
        foreach ($allGradeBooks as $gradebook) {
2338
            $gradeBookList[$gradebook['id']] = $gradebook['name'];
2339
        }
2340
2341
        $translateUrl = api_get_path(WEB_CODE_PATH).'admin/skill_translate.php?';
2342
        $translateNameButton = '';
2343
        $translateCodeButton = '';
2344
        $skillId = null;
2345
        if (!empty($skillInfo)) {
2346
            $skillId = $skillInfo['id'];
2347
            $translateNameUrl = $translateUrl.http_build_query(['skill' => $skillId, 'action' => 'name']);
2348
            $translateCodeUrl = $translateUrl.http_build_query(['skill' => $skillId, 'action' => 'code']);
2349
            $translateNameButton = Display::toolbarButton(
2350
                get_lang('TranslateThisTerm'),
2351
                $translateNameUrl,
2352
                'language',
2353
                'link'
2354
            );
2355
            $translateCodeButton = Display::toolbarButton(
2356
                get_lang('TranslateThisTerm'),
2357
                $translateCodeUrl,
2358
                'language',
2359
                'link'
2360
            );
2361
        }
2362
2363
        $form->addText('name', [get_lang('Name'), $translateNameButton], true, ['id' => 'name']);
2364
        $form->addText('short_code', [get_lang('ShortCode'), $translateCodeButton], false, ['id' => 'short_code']);
2365
2366
        // Cannot change parent of root
2367
        if ($skillId != 1) {
2368
            $form->addSelect('parent_id', get_lang('Parent'), $skillList, ['id' => 'parent_id']);
2369
        }
2370
2371
        $form->addSelect(
2372
            'gradebook_id',
2373
            [get_lang('Gradebook'), get_lang('WithCertificate')],
2374
            $gradeBookList,
2375
            ['id' => 'gradebook_id', 'multiple' => 'multiple', 'size' => 10]
2376
        );
2377
        $form->addTextarea('description', get_lang('Description'), ['id' => 'description', 'rows' => 7]);
2378
        $form->addTextarea('criteria', get_lang('CriteriaToEarnTheBadge'), ['id' => 'criteria', 'rows' => 7]);
2379
2380
        // EXTRA FIELDS
2381
        $extraField = new ExtraField('skill');
2382
        $returnParams = $extraField->addElements($form, $skillId);
2383
2384
        if (empty($skillInfo)) {
2385
            $form->addButtonCreate(get_lang('Add'));
2386
        } else {
2387
            $form->addButtonUpdate(get_lang('Update'));
2388
        }
2389
        $form->addHidden('id', null);
2390
2391
        return $returnParams;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $returnParams could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
2392
    }
2393
2394
    /**
2395
     * @return string
2396
     */
2397
    public function getToolBar()
2398
    {
2399
        $toolbar = Display::url(
2400
            Display::return_icon(
2401
                'back.png',
2402
                get_lang('ManageSkills'),
2403
                null,
2404
                ICON_SIZE_MEDIUM
2405
            ),
2406
            api_get_path(WEB_CODE_PATH).'admin/skill_list.php'
2407
        );
2408
        $actions = '<div class="actions">'.$toolbar.'</div>';
2409
2410
        return $actions;
2411
    }
2412
2413
    /**
2414
     * @param \Chamilo\SkillBundle\Entity\SkillRelItem        $skillRelItem
2415
     * @param \Chamilo\SkillBundle\Entity\SkillRelItemRelUser $skillRelItemRelUser
2416
     * @param bool                                            $addHeader
2417
     *
2418
     * @return string
2419
     */
2420
    public static function getUserSkillStatusLabel($skillRelItem, $skillRelItemRelUser, $addHeader = true)
2421
    {
2422
        if (empty($skillRelItem)) {
2423
            return '';
2424
        }
2425
        $type = 'success';
2426
        if (empty($skillRelItemRelUser)) {
2427
            $type = 'danger';
2428
        }
2429
        $label = '';
2430
        $skill = $skillRelItem->getSkill();
2431
        if ($addHeader) {
2432
            $label .= '<span id="'.$skill->getId().'" class="user_skill" style="cursor:pointer">';
2433
        }
2434
        $label .= Display::label($skill->getName(), $type);
2435
        if ($addHeader) {
2436
            $label .= '</span>&nbsp;';
2437
        }
2438
2439
        return $label;
2440
    }
2441
2442
    /**
2443
     * Assign a user with a SkilRelItem object.
2444
     *
2445
     * @param FormValidator $form
2446
     * @param int           $typeId see ITEM_TYPE_* constants
2447
     * @param int           $itemId
2448
     * @param int           $userId
2449
     */
2450
    public static function addSkillsToUserForm(FormValidator $form, $typeId, $itemId, $userId, $resultId = 0, $addHeader = false)
2451
    {
2452
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2453
        if ($allowSkillInTools && !empty($typeId) && !empty($itemId) && !empty($userId)) {
2454
            $em = Database::getManager();
2455
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2456
                ['itemId' => $itemId, 'itemType' => $typeId]
2457
            );
2458
2459
            $skillRelUser = new SkillRelUser();
2460
            $skillUserList = $skillRelUser->getUserSkills($userId);
2461
            if (!empty($skillUserList)) {
2462
                $skillUserList = array_column($skillUserList, 'skill_id');
2463
            }
2464
2465
            $skills = '';
2466
            /** @var SkillRelItem $skillRelItem */
2467
            foreach ($items as $skillRelItem) {
2468
                $criteria = [
2469
                    'user' => $userId,
2470
                    'skillRelItem' => $skillRelItem,
2471
                ];
2472
                $skillRelItemRelUser = $em->getRepository('ChamiloSkillBundle:SkillRelItemRelUser')->findOneBy($criteria);
2473
                $skills .= self::getUserSkillStatusLabel($skillRelItem, $skillRelItemRelUser);
2474
            }
2475
2476
            if (!empty($skills)) {
2477
                $url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=update_skill_rel_user&'.api_get_cidreq();
2478
                $params = [
2479
                    'item_id' => $itemId,
2480
                    'type_id' => $typeId,
2481
                    'user_id' => $userId,
2482
                    'course_id' => api_get_course_int_id(),
2483
                    'session_id' => api_get_session_id(),
2484
                    'result_id' => $resultId,
2485
                ];
2486
                $params = json_encode($params);
2487
                if ($addHeader) {
2488
                    $form->addHtml(Display::page_subheader2(get_lang('Skills')));
2489
                }
2490
                $html = '
2491
                <script>
2492
                    $(function() {
2493
                        $(".user_skill").on("click", function() {
2494
                            var skillId = this.id;
2495
                            var params = '.$params.';
2496
                            $.ajax({
2497
                                type: "GET",
2498
                                async: false,
2499
                                data: params,
2500
                                url: "'.$url.'&skill_id="+skillId,
2501
                                success: function(result) {
2502
                                    $("#" +skillId+ ".user_skill").html(result);
2503
                                }
2504
                            });                            
2505
                        });                        
2506
                    });
2507
                </script>
2508
                ';
2509
                $form->addHtml($html);
2510
                $form->addLabel(get_lang('Skills'), $skills);
2511
                if ($addHeader) {
2512
                    $form->addHtml('<br />');
2513
                }
2514
            }
2515
        }
2516
    }
2517
2518
    /**
2519
     * Add skills select ajax for an item (exercise, lp).
2520
     *
2521
     * @param FormValidator $form
2522
     * @param int           $typeId see ITEM_TYPE_* constants
2523
     * @param int           $itemId
2524
     *
2525
     * @throws Exception
2526
     *
2527
     * @return array
2528
     */
2529
    public static function addSkillsToForm(FormValidator $form, $typeId, $itemId = 0)
2530
    {
2531
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2532
        if (!$allowSkillInTools) {
2533
            return [];
2534
        }
2535
2536
        $skillList = [];
2537
        if (!empty($itemId)) {
2538
            $em = Database::getManager();
2539
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2540
                ['itemId' => $itemId, 'itemType' => $typeId]
2541
            );
2542
            /** @var SkillRelItem $skillRelItem */
2543
            foreach ($items as $skillRelItem) {
2544
                $skillList[$skillRelItem->getSkill()->getId()] = $skillRelItem->getSkill()->getName();
2545
            }
2546
        }
2547
2548
        $courseId = api_get_course_int_id();
2549
        $sessionId = api_get_session_id();
2550
2551
        $url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=search_skills_in_course&course_id='.$courseId.'&session_id='.$sessionId;
2552
        $form->addSelectAjax(
2553
            'skills',
2554
            get_lang('Skills'),
2555
            $skillList,
2556
            [
2557
                'url' => $url,
2558
                'multiple' => 'multiple',
2559
            ]
2560
        );
2561
2562
        return $skillList;
2563
    }
2564
2565
    /**
2566
     * @param int $courseId
2567
     * @param int $sessionId
2568
     *
2569
     * @return array
2570
     */
2571
    public static function getSkillRelItemsPerCourse($courseId, $sessionId = null)
2572
    {
2573
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2574
        $skills = [];
2575
2576
        if (empty($sessionId)) {
2577
            $sessionId = null;
2578
        }
2579
2580
        if ($allowSkillInTools) {
2581
            $em = Database::getManager();
2582
            $skills = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2583
                ['courseId' => $courseId, 'sessionId' => $sessionId]
2584
            );
2585
        }
2586
2587
        return $skills;
2588
    }
2589
2590
    /**
2591
     * @param int $itemId
2592
     * @param int $itemType
2593
     *
2594
     * @return array
2595
     */
2596
    public static function getItemInfo($itemId, $itemType)
2597
    {
2598
        $itemInfo = [];
2599
        $itemId = (int) $itemId;
2600
        $itemType = (int) $itemType;
2601
        $em = Database::getManager();
2602
2603
        switch ($itemType) {
2604
            case ITEM_TYPE_EXERCISE:
2605
                /** @var \Chamilo\CourseBundle\Entity\CQuiz $item */
2606
                $item = $em->getRepository('ChamiloCourseBundle:CQuiz')->find($itemId);
2607
                if ($item) {
2608
                    $itemInfo['name'] = $item->getTitle();
2609
                }
2610
                break;
2611
            case ITEM_TYPE_HOTPOTATOES:
2612
                break;
2613
            case ITEM_TYPE_LINK:
2614
                /** @var \Chamilo\CourseBundle\Entity\CLink $item */
2615
                $item = $em->getRepository('ChamiloCourseBundle:CLink')->find($itemId);
2616
                if ($item) {
2617
                    $itemInfo['name'] = $item->getTitle();
2618
                }
2619
                break;
2620
            case ITEM_TYPE_LEARNPATH:
2621
                /** @var \Chamilo\CourseBundle\Entity\CLp $item */
2622
                $item = $em->getRepository('ChamiloCourseBundle:CLp')->find($itemId);
2623
                if ($item) {
2624
                    $itemInfo['name'] = $item->getName();
2625
                }
2626
                break;
2627
            case ITEM_TYPE_GRADEBOOK:
2628
                break;
2629
            case ITEM_TYPE_STUDENT_PUBLICATION:
2630
                /** @var \Chamilo\CourseBundle\Entity\CStudentPublication $item */
2631
                $item = $em->getRepository('ChamiloCourseBundle:CStudentPublication')->find($itemId);
2632
                if ($item) {
2633
                    $itemInfo['name'] = $item->getTitle();
2634
                }
2635
                break;
2636
            //ITEM_TYPE_FORUM', 7);
2637
            case ITEM_TYPE_ATTENDANCE:
2638
                /** @var \Chamilo\CourseBundle\Entity\CAttendance $item */
2639
                $item = $em->getRepository('ChamiloCourseBundle:CAttendance')->find($itemId);
2640
                if ($item) {
2641
                    $itemInfo['name'] = $item->getName();
2642
                }
2643
                break;
2644
            case ITEM_TYPE_SURVEY:
2645
                /** @var \Chamilo\CourseBundle\Entity\CSurvey $item */
2646
                $item = $em->getRepository('ChamiloCourseBundle:CSurvey')->find($itemId);
2647
                if ($item) {
2648
                    $itemInfo['name'] = strip_tags($item->getTitle());
2649
                }
2650
                break;
2651
            case ITEM_TYPE_FORUM_THREAD:
2652
                /** @var \Chamilo\CourseBundle\Entity\CForumThread $item */
2653
                $item = $em->getRepository('ChamiloCourseBundle:CForumThread')->find($itemId);
2654
                if ($item) {
2655
                    $itemInfo['name'] = $item->getThreadTitle();
2656
                }
2657
                break;
2658
        }
2659
2660
        return $itemInfo;
2661
    }
2662
2663
    /**
2664
     * @param int $typeId
2665
     * @param int $itemId
2666
     *
2667
     * @return array
2668
     */
2669
    public static function getSkillRelItems($typeId, $itemId)
2670
    {
2671
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2672
        $skills = [];
2673
        if ($allowSkillInTools) {
2674
            $em = Database::getManager();
2675
            $skills = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2676
                ['itemId' => $itemId, 'itemType' => $typeId]
2677
            );
2678
        }
2679
2680
        return $skills;
2681
    }
2682
2683
    /**
2684
     * @param int $typeId
2685
     * @param int $itemId
2686
     *
2687
     * @return string
2688
     */
2689
    public static function getSkillRelItemsToString($typeId, $itemId)
2690
    {
2691
        $skills = self::getSkillRelItems($typeId, $itemId);
2692
        $skillToString = '';
2693
        if (!empty($skills)) {
2694
            /** @var SkillRelItem $skillRelItem */
2695
            $skillList = [];
2696
            foreach ($skills as $skillRelItem) {
2697
                $skillList[] = Display::label($skillRelItem->getSkill()->getName(), 'success');
2698
            }
2699
            $skillToString = '&nbsp;'.implode(' ', $skillList);
2700
        }
2701
2702
        return $skillToString;
2703
    }
2704
2705
    /**
2706
     * @param int $itemId
2707
     * @param int $typeId
2708
     */
2709
    public static function deleteSkillsFromItem($itemId, $typeId)
2710
    {
2711
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2712
        if ($allowSkillInTools) {
2713
            $itemId = (int) $itemId;
2714
            $typeId = (int) $typeId;
2715
2716
            $em = Database::getManager();
2717
            // Delete old ones
2718
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2719
                ['itemId' => $itemId, 'itemType' => $typeId]
2720
            );
2721
2722
            /** @var SkillRelItem $skillRelItem */
2723
            foreach ($items as $skillRelItem) {
2724
                $em->remove($skillRelItem);
2725
            }
2726
            $em->flush();
2727
        }
2728
    }
2729
2730
    /**
2731
     * Relate skill with an item (exercise, gradebook, lp, etc).
2732
     *
2733
     * @param FormValidator $form
2734
     * @param int           $typeId
2735
     * @param int           $itemId
2736
     *
2737
     * @throws \Doctrine\ORM\OptimisticLockException
2738
     */
2739
    public static function saveSkills($form, $typeId, $itemId)
2740
    {
2741
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2742
        if ($allowSkillInTools) {
2743
            $userId = api_get_user_id();
2744
            $courseId = api_get_course_int_id();
2745
            if (empty($courseId)) {
2746
                $courseId = null;
2747
            }
2748
            $sessionId = api_get_session_id();
2749
            if (empty($sessionId)) {
2750
                $sessionId = null;
2751
            }
2752
2753
            $em = Database::getManager();
2754
            $skills = (array) $form->getSubmitValue('skills');
2755
2756
            // Delete old ones
2757
            $items = $em->getRepository('ChamiloSkillBundle:SkillRelItem')->findBy(
2758
                ['itemId' => $itemId, 'itemType' => $typeId]
2759
            );
2760
            if (!empty($items)) {
2761
                /** @var SkillRelItem $skillRelItem */
2762
                foreach ($items as $skillRelItem) {
2763
                    if (!in_array($skillRelItem->getSkill()->getId(), $skills)) {
2764
                        $em->remove($skillRelItem);
2765
                    }
2766
                }
2767
                $em->flush();
2768
            }
2769
2770
            // Add new one
2771
            if (!empty($skills)) {
2772
                foreach ($skills as $skillId) {
2773
                    /** @var SkillEntity $skill */
2774
                    $skill = $em->getRepository('ChamiloCoreBundle:Skill')->find($skillId);
2775
                    if ($skill) {
2776
                        if (!$skill->hasItem($typeId, $itemId)) {
2777
                            $skillRelItem = new SkillRelItem();
2778
                            $skillRelItem
2779
                                ->setItemType($typeId)
2780
                                ->setItemId($itemId)
2781
                                ->setCourseId($courseId)
2782
                                ->setSessionId($sessionId)
2783
                                ->setCreatedBy($userId)
2784
                                ->setUpdatedBy($userId)
2785
                            ;
2786
                            $skill->addItem($skillRelItem);
2787
                            $em->persist($skill);
2788
                            $em->flush();
2789
                        }
2790
                    }
2791
                }
2792
            }
2793
        }
2794
    }
2795
2796
    /**
2797
     * Relate skill with an item (exercise, gradebook, lp, etc).
2798
     *
2799
     * @param FormValidator $form
2800
     *
2801
     * @return bool
2802
     */
2803
    public static function saveSkillsToCourseFromForm(FormValidator $form)
2804
    {
2805
        $skills = (array) $form->getSubmitValue('skills');
2806
        $courseId = (int) $form->getSubmitValue('course_id');
2807
        $sessionId = $form->getSubmitValue('session_id');
2808
2809
        return self::saveSkillsToCourse($skills, $courseId, $sessionId);
2810
    }
2811
2812
    /**
2813
     * @param array $skills
2814
     * @param int   $courseId
2815
     * @param int   $sessionId
2816
     *
2817
     * @throws \Doctrine\ORM\OptimisticLockException
2818
     *
2819
     * @return bool
2820
     */
2821
    public static function saveSkillsToCourse($skills, $courseId, $sessionId)
2822
    {
2823
        $allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
2824
        if (!$allowSkillInTools) {
2825
            return false;
2826
        }
2827
2828
        $em = Database::getManager();
2829
        $sessionId = empty($sessionId) ? null : (int) $sessionId;
2830
2831
        $course = api_get_course_entity($courseId);
2832
        if (empty($course)) {
2833
            return false;
2834
        }
2835
        $session = null;
2836
        if (!empty($sessionId)) {
2837
            $session = api_get_session_entity($sessionId);
2838
            $courseExistsInSession = SessionManager::sessionHasCourse($sessionId, $course->getCode());
2839
            if (!$courseExistsInSession) {
2840
                return false;
2841
            }
2842
        }
2843
2844
        // Delete old ones
2845
        $items = $em->getRepository('ChamiloSkillBundle:SkillRelCourse')->findBy(
2846
            ['course' => $courseId, 'session' => $sessionId]
2847
        );
2848
2849
        if (!empty($items)) {
2850
            /** @var SkillRelCourse $item */
2851
            foreach ($items as $item) {
2852
                if (!in_array($item->getSkill()->getId(), $skills)) {
2853
                    $em->remove($item);
2854
                }
2855
            }
2856
            $em->flush();
2857
        }
2858
2859
        // Add new one
2860
        if (!empty($skills)) {
2861
            foreach ($skills as $skillId) {
2862
                $item = new SkillRelCourse();
2863
                $item->setCourse($course);
2864
                $item->setSession($session);
2865
2866
                /** @var SkillEntity $skill */
2867
                $skill = $em->getRepository('ChamiloCoreBundle:Skill')->find($skillId);
2868
                if ($skill) {
2869
                    if (!$skill->hasCourseAndSession($item)) {
2870
                        $skill->addToCourse($item);
2871
                        $em->persist($skill);
2872
                    }
2873
                }
2874
            }
2875
            $em->flush();
2876
        }
2877
2878
        return true;
2879
    }
2880
}
2881