1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* For licensing terms, see /license.txt */ |
4
|
|
|
|
5
|
|
|
use Chamilo\CoreBundle\Entity\Asset; |
6
|
|
|
use Chamilo\CoreBundle\Entity\CourseCategory as CourseCategoryEntity; |
7
|
|
|
use Chamilo\CoreBundle\Framework\Container; |
8
|
|
|
use Chamilo\CoreBundle\Component\Utils\ActionIcon; |
9
|
|
|
use Chamilo\CoreBundle\Component\Utils\ObjectIcon; |
10
|
|
|
|
11
|
|
|
class CourseCategory |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* Get category details from a simple category code. |
15
|
|
|
* |
16
|
|
|
* @param string|null $categoryCode The literal category code |
17
|
|
|
* |
18
|
|
|
* @return array |
19
|
|
|
*/ |
20
|
|
|
public static function getCategory(string $categoryCode = null): array |
21
|
|
|
{ |
22
|
|
|
if (!empty($categoryCode)) { |
23
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
24
|
|
|
$categoryCode = Database::escape_string($categoryCode); |
25
|
|
|
$sql = "SELECT * FROM $table WHERE code ='$categoryCode'"; |
26
|
|
|
$result = Database::query($sql); |
27
|
|
|
|
28
|
|
|
if (Database::num_rows($result)) { |
29
|
|
|
$category = Database::fetch_assoc($result); |
30
|
|
|
if ($category) { |
31
|
|
|
// Get access url id |
32
|
|
|
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY); |
33
|
|
|
$sql = "SELECT * FROM $table WHERE course_category_id = ".$category['id']; |
34
|
|
|
$result = Database::query($sql); |
35
|
|
|
$result = Database::fetch_array($result); |
36
|
|
|
if ($result) { |
37
|
|
|
$category['access_url_id'] = $result['access_url_id']; |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
return $category; |
|
|
|
|
41
|
|
|
} |
42
|
|
|
} |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
return []; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Returns the category fields from the database from an int ID. |
50
|
|
|
* |
51
|
|
|
* @param int $categoryId The category ID |
52
|
|
|
* |
53
|
|
|
* @return array |
54
|
|
|
*/ |
55
|
|
|
public static function getCategoryById($categoryId) |
56
|
|
|
{ |
57
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
58
|
|
|
$categoryId = (int) $categoryId; |
59
|
|
|
$sql = "SELECT * FROM $table WHERE id = $categoryId"; |
60
|
|
|
$result = Database::query($sql); |
61
|
|
|
if (Database::num_rows($result)) { |
62
|
|
|
$category = Database::fetch_assoc($result); |
63
|
|
|
if ($category) { |
64
|
|
|
// Get access url id |
65
|
|
|
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY); |
66
|
|
|
$sql = "SELECT * FROM $table WHERE course_category_id = ".$category['id']; |
67
|
|
|
$result = Database::query($sql); |
68
|
|
|
$result = Database::fetch_array($result); |
69
|
|
|
if ($result) { |
70
|
|
|
$category['access_url_id'] = $result['access_url_id']; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
return $category; |
|
|
|
|
74
|
|
|
} |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
return []; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Returns a flat list of all course categories in this URL. If the |
82
|
|
|
* allow_base_course_category option is true, then also show the |
83
|
|
|
* course categories of the base URL. |
84
|
|
|
* |
85
|
|
|
* @return array [id, name, code, parent_id, tree_pos, children_count, number_courses] |
86
|
|
|
*/ |
87
|
|
|
public static function getAllCategories() |
88
|
|
|
{ |
89
|
|
|
$tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY); |
90
|
|
|
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY); |
91
|
|
|
$conditions = " INNER JOIN $table a ON (t1.id = a.course_category_id)"; |
92
|
|
|
$whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id(); |
93
|
|
|
$allowBaseCategories = ('true' === api_get_setting('course.allow_base_course_category')); |
94
|
|
|
if ($allowBaseCategories) { |
95
|
|
|
$whereCondition = " AND (a.access_url_id = ".api_get_current_access_url_id()." OR a.access_url_id = 1)"; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
$sql = "SELECT |
99
|
|
|
t1.id, |
100
|
|
|
t1.title, |
101
|
|
|
t1.code, |
102
|
|
|
t1.parent_id, |
103
|
|
|
t1.tree_pos, |
104
|
|
|
t1.children_count |
105
|
|
|
FROM $tbl_category t1 |
106
|
|
|
$conditions |
107
|
|
|
WHERE 1=1 |
108
|
|
|
$whereCondition |
109
|
|
|
GROUP BY |
110
|
|
|
t1.id, |
111
|
|
|
t1.title, |
112
|
|
|
t1.code, |
113
|
|
|
t1.parent_id, |
114
|
|
|
t1.tree_pos, |
115
|
|
|
t1.children_count |
116
|
|
|
ORDER BY t1.parent_id, t1.tree_pos"; |
117
|
|
|
|
118
|
|
|
$result = Database::query($sql); |
119
|
|
|
|
120
|
|
|
return Database::store_result($result, 'ASSOC'); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* @param string $code |
125
|
|
|
* @param string $name |
126
|
|
|
* @param string $canHaveCourses |
127
|
|
|
* @param int|null $parentId |
128
|
|
|
*/ |
129
|
|
|
public static function add($code, $name, $canHaveCourses, $description = '', $parentId = null): ?CourseCategoryEntity |
130
|
|
|
{ |
131
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
132
|
|
|
$code = trim($code); |
133
|
|
|
$name = trim($name); |
134
|
|
|
$parentId = (int) $parentId; |
135
|
|
|
|
136
|
|
|
$code = CourseManager::generate_course_code($code); |
137
|
|
|
$sql = "SELECT 1 FROM $table |
138
|
|
|
WHERE code = '".Database::escape_string($code)."'"; |
139
|
|
|
$result = Database::query($sql); |
140
|
|
|
if (Database::num_rows($result)) { |
141
|
|
|
return null; |
142
|
|
|
} |
143
|
|
|
$result = Database::query("SELECT MAX(tree_pos) AS maxTreePos FROM $table"); |
144
|
|
|
$row = Database::fetch_array($result); |
145
|
|
|
$tree_pos = $row['maxTreePos'] + 1; |
146
|
|
|
$parentId = empty($parentId) ? null : $parentId; |
147
|
|
|
|
148
|
|
|
$repo = Container::getCourseCategoryRepository(); |
149
|
|
|
$category = new CourseCategoryEntity(); |
150
|
|
|
$category |
151
|
|
|
->setTitle($name) |
152
|
|
|
->setCode($code) |
153
|
|
|
->setDescription($description) |
154
|
|
|
->setTreePos($tree_pos) |
155
|
|
|
->setAuthCourseChild($canHaveCourses) |
156
|
|
|
->setAuthCatChild('TRUE'); |
157
|
|
|
|
158
|
|
|
if (!empty($parentId)) { |
159
|
|
|
$category->setParent($repo->find($parentId)); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
$repo->save($category); |
163
|
|
|
|
164
|
|
|
$categoryId = $category->getId(); |
165
|
|
|
if ($categoryId) { |
166
|
|
|
self::updateParentCategoryChildrenCount($parentId, 1); |
167
|
|
|
UrlManager::addCourseCategoryListToUrl( |
168
|
|
|
[$categoryId], |
169
|
|
|
[api_get_current_access_url_id()] |
170
|
|
|
); |
171
|
|
|
|
172
|
|
|
return $category; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
return null; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Recursive function that updates the count of children in the parent. |
180
|
|
|
* |
181
|
|
|
* @param string $categoryId Category ID |
182
|
|
|
* @param int $delta The number to add or delete (1 to add one, -1 to remove one) |
183
|
|
|
*/ |
184
|
|
|
public static function updateParentCategoryChildrenCount($categoryId, $delta = 1) |
185
|
|
|
{ |
186
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
187
|
|
|
$categoryId = Database::escape_string($categoryId); |
188
|
|
|
$delta = (int) $delta; |
189
|
|
|
// First get to the highest level possible in the tree |
190
|
|
|
$result = Database::query("SELECT parent_id FROM $table WHERE id = '$categoryId'"); |
191
|
|
|
$row = Database::fetch_array($result); |
192
|
|
|
if (false !== $row && !empty($row['parent_id'])) { |
193
|
|
|
// if a parent was found, enter there to see if he's got one more parent |
194
|
|
|
self::updateParentCategoryChildrenCount($row['parent_id'], $delta); |
195
|
|
|
} |
196
|
|
|
// Now we're at the top, get back down to update each child |
197
|
|
|
$sql = "UPDATE $table SET children_count = (children_count - ".abs($delta).") WHERE id = '$categoryId'"; |
198
|
|
|
if ($delta >= 0) { |
199
|
|
|
$sql = "UPDATE $table SET children_count = (children_count + $delta) WHERE id = '$categoryId'"; |
200
|
|
|
} |
201
|
|
|
Database::query($sql); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
public static function edit($categoryId, $name, $canHaveCourses, $code, $description, $parentId = null): ?CourseCategoryEntity |
205
|
|
|
{ |
206
|
|
|
$repo = Container::getCourseCategoryRepository(); |
207
|
|
|
$category = $repo->find($categoryId); |
208
|
|
|
if (null === $category) { |
209
|
|
|
return null; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
$name = trim($name); |
213
|
|
|
$category |
214
|
|
|
->setCode($name) |
215
|
|
|
->setTitle($name) |
216
|
|
|
->setDescription($description) |
217
|
|
|
->setAuthCourseChild($canHaveCourses) |
218
|
|
|
; |
219
|
|
|
|
220
|
|
|
if (!empty($parentId)) { |
221
|
|
|
$category->setParent(Container::getCourseCategoryRepository()->find($parentId)); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
$repo->save($category); |
225
|
|
|
|
226
|
|
|
// Updating children |
227
|
|
|
/*$sql = "UPDATE $tbl_category SET parent_id = '$code' |
228
|
|
|
WHERE parent_id = '$old_code'"; |
229
|
|
|
Database::query($sql);*/ |
230
|
|
|
|
231
|
|
|
return $category; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* Move a node up on display. |
236
|
|
|
* |
237
|
|
|
* @param string $code |
238
|
|
|
* @param int $tree_pos |
239
|
|
|
* @param string $parent_id |
240
|
|
|
* |
241
|
|
|
* @return bool |
242
|
|
|
*/ |
243
|
|
|
public static function moveNodeUp($categoryId, $treePos, $parentId): bool |
244
|
|
|
{ |
245
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
246
|
|
|
$categoryId = (int) $categoryId; |
247
|
|
|
$treePos = (int) $treePos; |
248
|
|
|
|
249
|
|
|
$parentIdCondition = "parent_id IS NULL"; |
250
|
|
|
if (!empty($parentId)) { |
251
|
|
|
$parentIdCondition = "parent_id = '".Database::escape_string($parentId)."'"; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
self::reorganizeTreePos($parentId); |
255
|
|
|
|
256
|
|
|
$sql = "SELECT id, tree_pos |
257
|
|
|
FROM $table |
258
|
|
|
WHERE $parentIdCondition AND tree_pos < $treePos |
259
|
|
|
ORDER BY tree_pos DESC |
260
|
|
|
LIMIT 1"; |
261
|
|
|
|
262
|
|
|
$result = Database::query($sql); |
263
|
|
|
$previousCategory = Database::fetch_array($result); |
264
|
|
|
|
265
|
|
|
if (!$previousCategory) { |
266
|
|
|
return false; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
Database::query("UPDATE $table SET tree_pos = {$previousCategory['tree_pos']} WHERE id = $categoryId"); |
270
|
|
|
Database::query("UPDATE $table SET tree_pos = $treePos WHERE id = {$previousCategory['id']}"); |
271
|
|
|
|
272
|
|
|
return true; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
public static function moveNodeDown($categoryId, $treePos, $parentId): bool |
276
|
|
|
{ |
277
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
278
|
|
|
$categoryId = (int) $categoryId; |
279
|
|
|
$treePos = (int) $treePos; |
280
|
|
|
|
281
|
|
|
$parentIdCondition = "parent_id IS NULL"; |
282
|
|
|
if (!empty($parentId)) { |
283
|
|
|
$parentIdCondition = "parent_id = '".Database::escape_string($parentId)."'"; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
self::reorganizeTreePos($parentId); |
287
|
|
|
|
288
|
|
|
$sql = "SELECT id, tree_pos |
289
|
|
|
FROM $table |
290
|
|
|
WHERE $parentIdCondition AND tree_pos > $treePos |
291
|
|
|
ORDER BY tree_pos ASC |
292
|
|
|
LIMIT 1"; |
293
|
|
|
|
294
|
|
|
$result = Database::query($sql); |
295
|
|
|
$nextCategory = Database::fetch_array($result); |
296
|
|
|
|
297
|
|
|
if (!$nextCategory) { |
298
|
|
|
return false; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
Database::query("UPDATE $table SET tree_pos = {$nextCategory['tree_pos']} WHERE id = $categoryId"); |
302
|
|
|
Database::query("UPDATE $table SET tree_pos = $treePos WHERE id = {$nextCategory['id']}"); |
303
|
|
|
|
304
|
|
|
return true; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
public static function reorganizeTreePos($parentId): void |
308
|
|
|
{ |
309
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
310
|
|
|
|
311
|
|
|
$parentIdCondition = "parent_id IS NULL"; |
312
|
|
|
if (!empty($parentId)) { |
313
|
|
|
$parentIdCondition = "parent_id = '".Database::escape_string($parentId)."'"; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
$sql = "SELECT id FROM $table WHERE $parentIdCondition ORDER BY tree_pos"; |
317
|
|
|
$result = Database::query($sql); |
318
|
|
|
|
319
|
|
|
$newTreePos = 1; |
320
|
|
|
while ($row = Database::fetch_array($result)) { |
321
|
|
|
Database::query("UPDATE $table SET tree_pos = $newTreePos WHERE id = {$row['id']}"); |
322
|
|
|
$newTreePos++; |
323
|
|
|
} |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* @param string $categoryCode |
328
|
|
|
* |
329
|
|
|
* @return array |
330
|
|
|
*/ |
331
|
|
|
public static function getChildren($categoryCode) |
332
|
|
|
{ |
333
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
334
|
|
|
$categoryCode = Database::escape_string($categoryCode); |
335
|
|
|
$sql = "SELECT code, id FROM $table |
336
|
|
|
WHERE parent_id = '$categoryCode'"; |
337
|
|
|
$result = Database::query($sql); |
338
|
|
|
$children = []; |
339
|
|
|
while ($row = Database::fetch_assoc($result)) { |
340
|
|
|
$children[] = $row; |
341
|
|
|
$subChildren = self::getChildren($row['code']); |
342
|
|
|
$children = array_merge($children, $subChildren); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
return $children; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* @param string $categoryCode |
350
|
|
|
* |
351
|
|
|
* @return string|null |
352
|
|
|
*/ |
353
|
|
|
public static function getParentsToString($categoryCode) |
354
|
|
|
{ |
355
|
|
|
$parents = self::getParents($categoryCode); |
356
|
|
|
|
357
|
|
|
if (!empty($parents)) { |
358
|
|
|
$parents = array_reverse($parents); |
359
|
|
|
$categories = []; |
360
|
|
|
foreach ($parents as $category) { |
361
|
|
|
$categories[] = $category['code']; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
return implode(' > ', $categories).' > '; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
return null; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* @param string $categoryCode |
372
|
|
|
* |
373
|
|
|
* @return array |
374
|
|
|
*/ |
375
|
|
|
public static function getParents($categoryCode) |
376
|
|
|
{ |
377
|
|
|
if (empty($categoryCode)) { |
378
|
|
|
return []; |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
382
|
|
|
$categoryCode = Database::escape_string($categoryCode); |
383
|
|
|
$sql = "SELECT code, parent_id |
384
|
|
|
FROM $table |
385
|
|
|
WHERE code = '$categoryCode'"; |
386
|
|
|
|
387
|
|
|
$result = Database::query($sql); |
388
|
|
|
$children = []; |
389
|
|
|
while ($row = Database::fetch_assoc($result)) { |
390
|
|
|
$parent = self::getCategory($row['parent_id']); |
391
|
|
|
$children[] = $row; |
392
|
|
|
$subChildren = self::getParents($parent ? $parent['code'] : null); |
393
|
|
|
$children = array_merge($children, $subChildren); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
return $children; |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
public static function listCategories(array $categorySource = []): string |
400
|
|
|
{ |
401
|
|
|
$categories = self::getCategories($categorySource ? $categorySource['id'] : null); |
402
|
|
|
$categoryCode = $categorySource ? Security::remove_XSS($categorySource['code']) : ''; |
403
|
|
|
|
404
|
|
|
if (count($categories) > 0) { |
405
|
|
|
$table = new HTML_Table(['class' => 'data_table']); |
406
|
|
|
$column = 0; |
407
|
|
|
$row = 0; |
408
|
|
|
$headers = [ |
409
|
|
|
get_lang('Category'), |
410
|
|
|
get_lang('Sub-categories'), |
411
|
|
|
get_lang('Courses'), |
412
|
|
|
get_lang('Detail'), |
413
|
|
|
]; |
414
|
|
|
foreach ($headers as $header) { |
415
|
|
|
$table->setHeaderContents($row, $column, $header); |
416
|
|
|
$column++; |
417
|
|
|
} |
418
|
|
|
$row++; |
419
|
|
|
$baseUrl = api_get_path(WEB_CODE_PATH).'admin/course_category.php'; |
420
|
|
|
$baseParams = []; |
421
|
|
|
if (!empty($categorySource['id'])) { |
422
|
|
|
$baseParams['id'] = (int) $categorySource['id']; |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
$editIcon = Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Edit')); |
426
|
|
|
$exportIcon = Display::getMdiIcon(ActionIcon::EXPORT_CSV, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('ExportAsCSV')); |
427
|
|
|
$deleteIcon = Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')); |
428
|
|
|
$urlId = api_get_current_access_url_id(); |
429
|
|
|
|
430
|
|
|
$positions = array_map(fn($c) => $c->getTreePos(), $categories); |
431
|
|
|
$minTreePos = min($positions); |
432
|
|
|
$maxTreePos = max($positions); |
433
|
|
|
|
434
|
|
|
foreach ($categories as $category) { |
435
|
|
|
$categoryId = $category->getId(); |
436
|
|
|
$code = $category->getCode(); |
437
|
|
|
$treePos = $category->getTreePos(); |
438
|
|
|
$editUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [ |
439
|
|
|
'action' => 'edit', |
440
|
|
|
'id' => $categoryId, |
441
|
|
|
])); |
442
|
|
|
|
443
|
|
|
$moveUpUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [ |
444
|
|
|
'action' => 'moveUp', |
445
|
|
|
'id' => $categoryId, |
446
|
|
|
'tree_pos' => $treePos, |
447
|
|
|
])); |
448
|
|
|
|
449
|
|
|
$moveDownUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [ |
450
|
|
|
'action' => 'moveDown', |
451
|
|
|
'id' => $categoryId, |
452
|
|
|
'tree_pos' => $treePos, |
453
|
|
|
])); |
454
|
|
|
|
455
|
|
|
$deleteUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [ |
456
|
|
|
'action' => 'delete', |
457
|
|
|
'id' => $categoryId, |
458
|
|
|
])); |
459
|
|
|
|
460
|
|
|
$exportUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [ |
461
|
|
|
'action' => 'export', |
462
|
|
|
'id' => $categoryId, |
463
|
|
|
])); |
464
|
|
|
|
465
|
|
|
$actions = []; |
466
|
|
|
|
467
|
|
|
$inUrl = $category->getUrls()->filter( |
468
|
|
|
function ($entry) use ($urlId) { |
469
|
|
|
return $entry->getUrl()->getId() === $urlId; |
470
|
|
|
} |
471
|
|
|
); |
472
|
|
|
|
473
|
|
|
if ($inUrl->count() > 0) { |
474
|
|
|
$actions[] = Display::url($editIcon, $editUrl); |
475
|
|
|
|
476
|
|
|
if ($treePos > $minTreePos) { |
477
|
|
|
$actions[] = Display::url( |
478
|
|
|
Display::getMdiIcon(ActionIcon::UP, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Move up')), |
479
|
|
|
$moveUpUrl |
480
|
|
|
); |
481
|
|
|
} else { |
482
|
|
|
$actions[] = Display::getMdiIcon(ActionIcon::UP, 'ch-tool-icon-disabled', null, ICON_SIZE_SMALL, get_lang('Move up')); |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
if ($treePos < $maxTreePos) { |
486
|
|
|
$actions[] = Display::url( |
487
|
|
|
Display::getMdiIcon(ActionIcon::DOWN, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Move down')), |
488
|
|
|
$moveDownUrl |
489
|
|
|
); |
490
|
|
|
} else { |
491
|
|
|
$actions[] = Display::getMdiIcon(ActionIcon::DOWN, 'ch-tool-icon-disabled', null, ICON_SIZE_SMALL, get_lang('Move down')); |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
$actions[] = Display::url($exportIcon, $exportUrl); |
495
|
|
|
$actions[] = Display::url( |
496
|
|
|
$deleteIcon, |
497
|
|
|
$deleteUrl, |
498
|
|
|
[ |
499
|
|
|
'onclick' => 'javascript: if (!confirm(\''.addslashes( |
500
|
|
|
api_htmlentities(sprintf(get_lang('Please confirm your choice')), ENT_QUOTES) |
501
|
|
|
).'\')) return false;', |
502
|
|
|
] |
503
|
|
|
); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
$url = api_get_path(WEB_CODE_PATH).'admin/course_category.php?id='.$categoryId; |
507
|
|
|
$title = Display::url( |
508
|
|
|
Display::getMdiIcon( |
509
|
|
|
ObjectIcon::FOLDER, |
510
|
|
|
'ch-tool-icon', |
511
|
|
|
null, |
512
|
|
|
ICON_SIZE_SMALL, |
513
|
|
|
get_lang('Open this category') |
514
|
|
|
).' '.$category->getTitle().' ('.$code.')', |
515
|
|
|
$url |
516
|
|
|
); |
517
|
|
|
|
518
|
|
|
$countCourses = $category->getCourses()->count(); |
519
|
|
|
$content = [ |
520
|
|
|
$title, |
521
|
|
|
$category->getChildrenCount(), |
522
|
|
|
$countCourses, |
523
|
|
|
implode('', $actions), |
524
|
|
|
]; |
525
|
|
|
$column = 0; |
526
|
|
|
foreach ($content as $value) { |
527
|
|
|
$table->setCellContents($row, $column, $value); |
528
|
|
|
$column++; |
529
|
|
|
} |
530
|
|
|
$row++; |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
return $table->toHtml(); |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
return Display::return_message(get_lang('There are no categories here'), 'warning'); |
537
|
|
|
} |
538
|
|
|
|
539
|
|
|
/** |
540
|
|
|
* @param int|null $category Optional. Parent category ID. |
541
|
|
|
* |
542
|
|
|
* @return CourseCategoryEntity[] |
543
|
|
|
*/ |
544
|
|
|
public static function getCategories($category = null) |
545
|
|
|
{ |
546
|
|
|
$repo = Container::getCourseCategoryRepository(); |
547
|
|
|
$category = (int) $category; |
548
|
|
|
$allowBaseCourseCategory = ('true' === api_get_setting('course.allow_base_course_category')); |
549
|
|
|
|
550
|
|
|
return $repo->findAllInAccessUrl( |
551
|
|
|
api_get_current_access_url_id(), |
552
|
|
|
$allowBaseCourseCategory, |
553
|
|
|
$category |
554
|
|
|
); |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
/** |
558
|
|
|
* @return array |
559
|
|
|
*/ |
560
|
|
|
public static function getCategoriesToDisplayInHomePage() |
561
|
|
|
{ |
562
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
563
|
|
|
$sql = "SELECT title FROM $table |
564
|
|
|
WHERE parent_id IS NULL |
565
|
|
|
ORDER BY tree_pos"; |
566
|
|
|
|
567
|
|
|
return Database::store_result(Database::query($sql)); |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
/** |
571
|
|
|
* @param string $categoryCode |
572
|
|
|
* |
573
|
|
|
* @return array |
574
|
|
|
*/ |
575
|
|
|
public static function getCategoriesCanBeAddedInCourse($categoryCode) |
576
|
|
|
{ |
577
|
|
|
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY); |
578
|
|
|
$conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)"; |
579
|
|
|
$whereCondition = ' AND a.access_url_id = '.api_get_current_access_url_id(); |
580
|
|
|
|
581
|
|
|
$tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY); |
582
|
|
|
$sql = "SELECT c.id, c.code, c.title |
583
|
|
|
FROM $tbl_category c |
584
|
|
|
$conditions |
585
|
|
|
WHERE (auth_course_child = 'TRUE' OR code = '".Database::escape_string($categoryCode)."') |
586
|
|
|
$whereCondition |
587
|
|
|
ORDER BY tree_pos"; |
588
|
|
|
$res = Database::query($sql); |
589
|
|
|
|
590
|
|
|
$categoryToAvoid = ''; |
591
|
|
|
if (!api_is_platform_admin()) { |
592
|
|
|
$categoryToAvoid = api_get_setting('course.course_category_code_to_use_as_model'); |
593
|
|
|
} |
594
|
|
|
$categories[''] = '-'; |
|
|
|
|
595
|
|
|
while ($cat = Database::fetch_array($res)) { |
596
|
|
|
$categoryCode = $cat['code']; |
597
|
|
|
if (!empty($categoryToAvoid) && $categoryToAvoid == $categoryCode) { |
598
|
|
|
continue; |
599
|
|
|
} |
600
|
|
|
$categories[$cat['id']] = '('.$cat['code'].') '.$cat['title']; |
601
|
|
|
ksort($categories); |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
return $categories; |
605
|
|
|
} |
606
|
|
|
|
607
|
|
|
/** |
608
|
|
|
* @param string $category_code |
609
|
|
|
* @param string $keyword |
610
|
|
|
* |
611
|
|
|
* @paran bool $avoidCourses |
612
|
|
|
* @paran array $conditions |
613
|
|
|
* |
614
|
|
|
* @return int |
615
|
|
|
*/ |
616
|
|
|
public static function countCoursesInCategory( |
617
|
|
|
$category_code = '', |
618
|
|
|
$keyword = '', |
619
|
|
|
$avoidCourses = true, |
620
|
|
|
$conditions = [] |
621
|
|
|
) { |
622
|
|
|
return self::getCoursesInCategory($category_code, $keyword, $avoidCourses, $conditions, true); |
623
|
|
|
} |
624
|
|
|
|
625
|
|
|
/** |
626
|
|
|
* @return \Chamilo\CoreBundle\Entity\Course[] |
627
|
|
|
*/ |
628
|
|
|
public static function getCoursesInCategory( |
629
|
|
|
$categoryId, |
630
|
|
|
$keyword = '', |
631
|
|
|
$avoidCourses = true, |
632
|
|
|
$conditions = [], |
633
|
|
|
$getCount = false |
634
|
|
|
) { |
635
|
|
|
$repo = Container::getCourseCategoryRepository(); |
636
|
|
|
/** @var CourseCategoryEntity $category */ |
637
|
|
|
$category = $repo->find($categoryId); |
638
|
|
|
|
639
|
|
|
// @todo add filters |
640
|
|
|
|
641
|
|
|
return $category->getCourses(); |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
/** |
645
|
|
|
* @param array $list |
646
|
|
|
* |
647
|
|
|
* @return array |
648
|
|
|
*/ |
649
|
|
|
public static function getCourseCategoryNotInList($list) |
650
|
|
|
{ |
651
|
|
|
$table = Database::get_main_table(TABLE_MAIN_CATEGORY); |
652
|
|
|
|
653
|
|
|
if (empty($list)) { |
654
|
|
|
$sql = "SELECT * FROM $table |
655
|
|
|
WHERE (parent_id IS NULL) "; |
656
|
|
|
$result = Database::query($sql); |
657
|
|
|
|
658
|
|
|
return Database::store_result($result, 'ASSOC'); |
659
|
|
|
} |
660
|
|
|
|
661
|
|
|
$list = array_map('intval', $list); |
662
|
|
|
$listToString = implode("','", $list); |
663
|
|
|
|
664
|
|
|
$sql = "SELECT * FROM $table |
665
|
|
|
WHERE id NOT IN ('$listToString') AND (parent_id IS NULL) "; |
666
|
|
|
$result = Database::query($sql); |
667
|
|
|
|
668
|
|
|
return Database::store_result($result, 'ASSOC'); |
669
|
|
|
} |
670
|
|
|
|
671
|
|
|
/** |
672
|
|
|
* @param string $keyword |
673
|
|
|
* |
674
|
|
|
* @return array|null |
675
|
|
|
*/ |
676
|
|
|
public static function searchCategoryByKeyword($keyword) |
677
|
|
|
{ |
678
|
|
|
if (empty($keyword)) { |
679
|
|
|
return null; |
680
|
|
|
} |
681
|
|
|
|
682
|
|
|
$tableCategory = Database::get_main_table(TABLE_MAIN_CATEGORY); |
683
|
|
|
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY); |
684
|
|
|
$conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)"; |
685
|
|
|
$whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id(); |
686
|
|
|
|
687
|
|
|
$allowBaseCategories = ('true' === api_get_setting('course.allow_base_course_category')); |
688
|
|
|
if ($allowBaseCategories) { |
689
|
|
|
$whereCondition = " AND (a.access_url_id = ".api_get_current_access_url_id()." OR a.access_url_id = 1) "; |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
$keyword = Database::escape_string($keyword); |
693
|
|
|
|
694
|
|
|
$sql = "SELECT c.*, c.title as text |
695
|
|
|
FROM $tableCategory c $conditions |
696
|
|
|
WHERE |
697
|
|
|
( |
698
|
|
|
c.code LIKE '%$keyword%' OR c.title LIKE '%$keyword%' |
699
|
|
|
) AND auth_course_child = 'TRUE' |
700
|
|
|
$whereCondition "; |
701
|
|
|
$result = Database::query($sql); |
702
|
|
|
|
703
|
|
|
return Database::store_result($result, 'ASSOC'); |
704
|
|
|
} |
705
|
|
|
|
706
|
|
|
/** |
707
|
|
|
* Get Pagination HTML div. |
708
|
|
|
* |
709
|
|
|
* @param $pageCurrent |
710
|
|
|
* @param $pageLength |
711
|
|
|
* @param $pageTotal |
712
|
|
|
* |
713
|
|
|
* @return string |
714
|
|
|
*/ |
715
|
|
|
public static function getCatalogPagination($pageCurrent, $pageLength, $pageTotal) |
716
|
|
|
{ |
717
|
|
|
// Start empty html |
718
|
|
|
$pageDiv = ''; |
719
|
|
|
$html = ''; |
720
|
|
|
$pageBottom = max(1, $pageCurrent - 3); |
721
|
|
|
$pageTop = min($pageTotal, $pageCurrent + 3); |
722
|
|
|
|
723
|
|
|
if ($pageBottom > 1) { |
724
|
|
|
$pageDiv .= self::getPageNumberItem(1, $pageLength); |
725
|
|
|
if ($pageBottom > 2) { |
726
|
|
|
$pageDiv .= self::getPageNumberItem( |
727
|
|
|
$pageBottom - 1, |
728
|
|
|
$pageLength, |
729
|
|
|
null, |
730
|
|
|
'...' |
731
|
|
|
); |
732
|
|
|
} |
733
|
|
|
} |
734
|
|
|
|
735
|
|
|
// For each page add its page button to html |
736
|
|
|
for ($i = $pageBottom; $i <= $pageTop; $i++) { |
737
|
|
|
if ($i === $pageCurrent) { |
738
|
|
|
$pageItemAttributes = ['class' => 'page-item active']; |
739
|
|
|
} else { |
740
|
|
|
$pageItemAttributes = ['class' => 'page-item']; |
741
|
|
|
} |
742
|
|
|
$pageDiv .= self::getPageNumberItem( |
743
|
|
|
$i, |
744
|
|
|
$pageLength, |
745
|
|
|
$pageItemAttributes |
746
|
|
|
); |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
// Check if current page is the last page |
750
|
|
|
if ($pageTop < $pageTotal) { |
751
|
|
|
if ($pageTop < ($pageTotal - 1)) { |
752
|
|
|
$pageDiv .= self::getPageNumberItem( |
753
|
|
|
$pageTop + 1, |
754
|
|
|
$pageLength, |
755
|
|
|
null, |
756
|
|
|
'...' |
757
|
|
|
); |
758
|
|
|
} |
759
|
|
|
$pageDiv .= self::getPageNumberItem($pageTotal, $pageLength); |
760
|
|
|
} |
761
|
|
|
|
762
|
|
|
// Complete pagination html |
763
|
|
|
$pageDiv = Display::tag('ul', $pageDiv, ['class' => 'pagination']); |
764
|
|
|
$html .= '<nav>'.$pageDiv.'</nav>'; |
765
|
|
|
|
766
|
|
|
return $html; |
767
|
|
|
} |
768
|
|
|
|
769
|
|
|
/** |
770
|
|
|
* Get li HTML of page number. |
771
|
|
|
* |
772
|
|
|
* @param $pageNumber |
773
|
|
|
* @param $pageLength |
774
|
|
|
* @param array $liAttributes |
775
|
|
|
* @param string $content |
776
|
|
|
* |
777
|
|
|
* @return string |
778
|
|
|
*/ |
779
|
|
|
public static function getPageNumberItem( |
780
|
|
|
$pageNumber, |
781
|
|
|
$pageLength, |
782
|
|
|
$liAttributes = [], |
783
|
|
|
$content = '' |
784
|
|
|
) { |
785
|
|
|
// Get page URL |
786
|
|
|
$url = self::getCourseCategoryUrl( |
787
|
|
|
$pageNumber, |
788
|
|
|
$pageLength |
789
|
|
|
); |
790
|
|
|
|
791
|
|
|
// If is current page ('active' class) clear URL |
792
|
|
|
if (isset($liAttributes) && is_array($liAttributes) && isset($liAttributes['class'])) { |
793
|
|
|
if (false !== strpos('active', $liAttributes['class'])) { |
794
|
|
|
$url = ''; |
795
|
|
|
} |
796
|
|
|
} |
797
|
|
|
|
798
|
|
|
$content = !empty($content) ? $content : $pageNumber; |
799
|
|
|
|
800
|
|
|
return Display::tag( |
801
|
|
|
'li', |
802
|
|
|
Display::url( |
803
|
|
|
$content, |
804
|
|
|
$url, |
805
|
|
|
['class' => 'page-link'] |
806
|
|
|
), |
807
|
|
|
$liAttributes |
808
|
|
|
); |
809
|
|
|
} |
810
|
|
|
|
811
|
|
|
/** |
812
|
|
|
* Return URL to course catalog. |
813
|
|
|
* |
814
|
|
|
* @param int $pageCurrent |
815
|
|
|
* @param int $pageLength |
816
|
|
|
* @param string $categoryCode |
817
|
|
|
* @param int $hiddenLinks |
818
|
|
|
* @param string $action |
819
|
|
|
* |
820
|
|
|
* @return string |
821
|
|
|
*/ |
822
|
|
|
public static function getCourseCategoryUrl( |
823
|
|
|
$pageCurrent, |
824
|
|
|
$pageLength, |
825
|
|
|
$categoryCode = null, |
826
|
|
|
$hiddenLinks = null, |
827
|
|
|
$action = null |
828
|
|
|
) { |
829
|
|
|
$requestAction = isset($_REQUEST['action']) ? Security::remove_XSS($_REQUEST['action']) : null; |
830
|
|
|
$action = isset($action) ? Security::remove_XSS($action) : $requestAction; |
831
|
|
|
$searchTerm = isset($_REQUEST['search_term']) ? Security::remove_XSS($_REQUEST['search_term']) : null; |
832
|
|
|
|
833
|
|
|
if ('subscribe_user_with_password' === $action) { |
834
|
|
|
$action = 'subscribe'; |
835
|
|
|
} |
836
|
|
|
|
837
|
|
|
$categoryCodeRequest = isset($_REQUEST['category_code']) ? Security::remove_XSS( |
838
|
|
|
$_REQUEST['category_code'] |
839
|
|
|
) : null; |
840
|
|
|
$categoryCode = isset($categoryCode) ? Security::remove_XSS($categoryCode) : $categoryCodeRequest; |
841
|
|
|
$hiddenLinksRequest = isset($_REQUEST['hidden_links']) ? Security::remove_XSS($_REQUEST['hidden_links']) : null; |
842
|
|
|
$hiddenLinks = isset($hiddenLinks) ? Security::remove_XSS($hiddenLinksRequest) : $categoryCodeRequest; |
843
|
|
|
|
844
|
|
|
// Start URL with params |
845
|
|
|
$pageUrl = api_get_self(). |
846
|
|
|
'?action='.$action. |
847
|
|
|
'&category_code='.$categoryCode. |
848
|
|
|
'&hidden_links='.$hiddenLinks. |
849
|
|
|
'&pageCurrent='.$pageCurrent. |
850
|
|
|
'&pageLength='.$pageLength; |
851
|
|
|
|
852
|
|
|
switch ($action) { |
853
|
|
|
case 'subscribe': |
854
|
|
|
// for search |
855
|
|
|
$pageUrl .= |
856
|
|
|
'&search_term='.$searchTerm. |
857
|
|
|
'&search_course=1'. |
858
|
|
|
'&sec_token='.Security::getTokenFromSession(); |
859
|
|
|
break; |
860
|
|
|
case 'display_courses': |
861
|
|
|
default: |
862
|
|
|
break; |
863
|
|
|
} |
864
|
|
|
|
865
|
|
|
return $pageUrl; |
866
|
|
|
} |
867
|
|
|
|
868
|
|
|
/** |
869
|
|
|
* Return the name tool by action. |
870
|
|
|
* |
871
|
|
|
* @param string $action |
872
|
|
|
* |
873
|
|
|
* @return string |
874
|
|
|
*/ |
875
|
|
|
public static function getCourseCatalogNameTools($action) |
876
|
|
|
{ |
877
|
|
|
$nameTools = get_lang('My courses'); |
878
|
|
|
if (empty($action)) { |
879
|
|
|
return $nameTools; //should never happen |
880
|
|
|
} |
881
|
|
|
|
882
|
|
|
switch ($action) { |
883
|
|
|
case 'subscribe': |
884
|
|
|
case 'subscribe_user_with_password': |
885
|
|
|
case 'display_random_courses': |
886
|
|
|
case 'display_courses': |
887
|
|
|
$nameTools = get_lang('Courses catalog'); |
888
|
|
|
break; |
889
|
|
|
case 'display_sessions': |
890
|
|
|
$nameTools = get_lang('Course sessions'); |
891
|
|
|
break; |
892
|
|
|
default: |
893
|
|
|
// Nothing to do |
894
|
|
|
break; |
895
|
|
|
} |
896
|
|
|
|
897
|
|
|
return $nameTools; |
898
|
|
|
} |
899
|
|
|
|
900
|
|
|
/** |
901
|
|
|
* Save image for a course category. |
902
|
|
|
* |
903
|
|
|
* @param array $fileData File data from $_FILES |
904
|
|
|
*/ |
905
|
|
|
public static function saveImage(CourseCategoryEntity $category, $fileData, $crop = '') |
906
|
|
|
{ |
907
|
|
|
if (isset($fileData['tmp_name']) && !empty($fileData['tmp_name'])) { |
908
|
|
|
$repo = Container::getCourseCategoryRepository(); |
909
|
|
|
$repo->deleteAsset($category); |
910
|
|
|
|
911
|
|
|
$assetRepo = Container::getAssetRepository(); |
912
|
|
|
$asset = (new Asset()) |
913
|
|
|
->setCategory(Asset::COURSE_CATEGORY) |
914
|
|
|
->setTitle($fileData['name']) |
915
|
|
|
->setCrop($crop) |
916
|
|
|
; |
917
|
|
|
$asset = $assetRepo->createFromRequest($asset, $fileData); |
918
|
|
|
|
919
|
|
|
$category->setAsset($asset); |
920
|
|
|
$repo->save($category); |
921
|
|
|
} |
922
|
|
|
} |
923
|
|
|
} |
924
|
|
|
|