Skip to content

Course: Add improvements and fixes in category management - refs #5985 #6233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 54 additions & 10 deletions public/main/admin/course_category.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@
CourseCategory::reorganizeTreePos($parentId);
Display::addFlash(Display::return_message(get_lang('Deleted')));
}
header('Location: '.api_get_self().'?category='.Security::remove_XSS($category));
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/course_category.php'.(
!empty($parentId) ? '?id='.(int) $parentId : ''
));
exit;
case 'export':
$courses = CourseCategory::getCoursesInCategory($categoryId);
if ($courses && count($courses) > 0) {
$name = api_get_local_time().'_'.$categoryInfo['code'];
$courseList = [];

/* @var \Chamilo\CoreBundle\Entity\Course $course */
/* @var Course $course */
foreach ($courses as $course) {
$courseList[] = [$course->getTitle()];
}
Expand All @@ -57,7 +59,9 @@
Export::arrayToCsvSimple($courseList, $name, false, $header);
} else {
Display::addFlash(Display::return_message(get_lang('No courses found for this category'), 'warning'));
header('Location: '.api_get_self().'?category='.Security::remove_XSS($categoryId));
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/course_category.php'.(
!empty($parentId) ? '?id='.(int) $parentId : ''
));
exit;
}
break;
Expand All @@ -67,7 +71,19 @@
} else {
Display::addFlash(Display::return_message(get_lang('Cannot move category up'), 'error'));
}
header('Location: '.api_get_self().'?category='.Security::remove_XSS($category));
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/course_category.php'.(
!empty($parentId) ? '?id='.(int) $parentId : ''
));
exit;
case 'moveDown':
if (CourseCategory::moveNodeDown($categoryId, $_GET['tree_pos'], $parentId)) {
Display::addFlash(Display::return_message(get_lang('Update successful')));
} else {
Display::addFlash(Display::return_message(get_lang('Cannot move category down'), 'error'));
}
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/course_category.php'.(
!empty($parentId) ? '?id='.(int) $parentId : ''
));
exit;
case 'add':
if (isset($_POST['formSent']) && $_POST['formSent']) {
Expand All @@ -76,15 +92,15 @@
$_POST['title'],
$_POST['auth_course_child'],
$_POST['description'],
$parentId,
$_POST['parent_id'] ?? null,
);

if (isset($_FILES['image']) && $categoryEntity) {
$crop = $_POST['picture_crop_result'] ?? '';
CourseCategory::saveImage($categoryEntity, $_FILES['image'], $crop);
}
Display::addFlash(Display::return_message(get_lang('Item added')));
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/course_category.php?id='.$parentId);
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/course_category.php'.(!empty($_POST['parent_id']) ? '?id='.(int) $_POST['parent_id'] : ''));
exit;
}
break;
Expand All @@ -95,7 +111,8 @@
$_REQUEST['title'],
$_REQUEST['auth_course_child'],
$_REQUEST['code'],
$_REQUEST['description']
$_REQUEST['description'],
$_REQUEST['parent_id'] ?? null
);

// Delete Picture Category
Expand All @@ -111,7 +128,7 @@
}

Display::addFlash(Display::return_message(get_lang('Update successful')));
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/course_category.php?id='.$parentId);
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/course_category.php'.(!empty($_POST['parent_id']) ? '?id='.(int) $_POST['parent_id'] : ''));
exit;
}
break;
Expand Down Expand Up @@ -173,7 +190,24 @@
),
];
$form->addGroup($group, null, get_lang('Allow adding courses in this category?'));

if ('add' === $action && !empty($categoryId)) {
$form->addHidden('parent_id', $categoryId);
$form->addLabel(get_lang('Parent category'), $parentInfo['title'].' ('.$parentInfo['code'].')');
} else {
$allCategories = CourseCategory::getAllCategories();
$parentOptions = ['' => get_lang('No parent')];
foreach ($allCategories as $cat) {
if ('edit' === $action && $cat['id'] == $categoryId) {
continue;
}
$parentOptions[$cat['id']] = '('.$cat['code'].') '.$cat['title'];
}
$form->addSelect(
'parent_id',
get_lang('Parent category'),
$parentOptions
);
}
$form->addHtmlEditor(
'description',
get_lang('Description'),
Expand All @@ -199,10 +233,20 @@
if ('edit' === $action && !empty($categoryInfo)) {
$text = get_lang('Save');
$form->setDefaults($categoryInfo);
$form->setDefaults([
'parent_id' => $categoryInfo['parent_id'] ?? '',
]);
$form->addButtonSave($text);
} else {
$text = get_lang('Add category');
$form->setDefaults(['auth_course_child' => 'TRUE']);
$defaultValues = [
'auth_course_child' => 'TRUE',
];

if ('add' === $action && !empty($categoryId)) {
$defaultValues['parent_id'] = $categoryId;
}
$form->setDefaults($defaultValues);
$form->addButtonCreate($text);
}
$form->display();
Expand Down
134 changes: 107 additions & 27 deletions public/main/inc/lib/course_category.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,12 @@ public static function getCategoryById($categoryId)
public static function getAllCategories()
{
$tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);

$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
$conditions = " INNER JOIN $table a ON (t1.id = a.course_category_id)";
$whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
$allowBaseCategories = ('true' === api_get_setting('course.allow_base_course_category'));
if ($allowBaseCategories) {
$whereCondition = " AND (a.access_url_id = ".api_get_current_access_url_id()." OR a.access_url_id = 1) ";
$whereCondition = " AND (a.access_url_id = ".api_get_current_access_url_id()." OR a.access_url_id = 1)";
}

$sql = "SELECT
Expand All @@ -103,21 +101,19 @@ public static function getAllCategories()
t1.code,
t1.parent_id,
t1.tree_pos,
t1.children_count,
COUNT(DISTINCT t3.code) AS number_courses
FROM $tbl_category t1
$conditions
LEFT JOIN $tbl_course t3
ON t3.category_id=t1.id
WHERE 1=1
$whereCondition
GROUP BY
t1.title,
t1.code,
t1.parent_id,
t1.tree_pos,
t1.children_count
ORDER BY t1.parent_id, t1.tree_pos";
t1.children_count
FROM $tbl_category t1
$conditions
WHERE 1=1
$whereCondition
GROUP BY
t1.id,
t1.title,
t1.code,
t1.parent_id,
t1.tree_pos,
t1.children_count
ORDER BY t1.parent_id, t1.tree_pos";

$result = Database::query($sql);

Expand Down Expand Up @@ -205,7 +201,7 @@ public static function updateParentCategoryChildrenCount($categoryId, $delta = 1
Database::query($sql);
}

public static function edit($categoryId, $name, $canHaveCourses, $code, $description): ?CourseCategoryEntity
public static function edit($categoryId, $name, $canHaveCourses, $code, $description, $parentId = null): ?CourseCategoryEntity
{
$repo = Container::getCourseCategoryRepository();
$category = $repo->find($categoryId);
Expand All @@ -221,6 +217,10 @@ public static function edit($categoryId, $name, $canHaveCourses, $code, $descrip
->setAuthCourseChild($canHaveCourses)
;

if (!empty($parentId)) {
$category->setParent(Container::getCourseCategoryRepository()->find($parentId));
}

$repo->save($category);

// Updating children
Expand Down Expand Up @@ -272,6 +272,38 @@ public static function moveNodeUp($categoryId, $treePos, $parentId): bool
return true;
}

public static function moveNodeDown($categoryId, $treePos, $parentId): bool
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
$categoryId = (int) $categoryId;
$treePos = (int) $treePos;

$parentIdCondition = "parent_id IS NULL";
if (!empty($parentId)) {
$parentIdCondition = "parent_id = '".Database::escape_string($parentId)."'";
}

self::reorganizeTreePos($parentId);

$sql = "SELECT id, tree_pos
FROM $table
WHERE $parentIdCondition AND tree_pos > $treePos
ORDER BY tree_pos ASC
LIMIT 1";

$result = Database::query($sql);
$nextCategory = Database::fetch_array($result);

if (!$nextCategory) {
return false;
}

Database::query("UPDATE $table SET tree_pos = {$nextCategory['tree_pos']} WHERE id = $categoryId");
Database::query("UPDATE $table SET tree_pos = $treePos WHERE id = {$nextCategory['id']}");

return true;
}

public static function reorganizeTreePos($parentId): void
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
Expand Down Expand Up @@ -384,21 +416,51 @@ public static function listCategories(array $categorySource = []): string
$column++;
}
$row++;
$mainUrl = api_get_path(WEB_CODE_PATH).'admin/course_category.php?category='.$categoryCode;
$baseUrl = api_get_path(WEB_CODE_PATH).'admin/course_category.php';
$baseParams = [];
if (!empty($categorySource['id'])) {
$baseParams['id'] = (int) $categorySource['id'];
}

$editIcon = Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Edit'));
$exportIcon = Display::getMdiIcon(ActionIcon::EXPORT_CSV, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('ExportAsCSV'));
$deleteIcon = Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete'));
$moveIcon = Display::getMdiIcon(ActionIcon::UP, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Up in same level'));

$urlId = api_get_current_access_url_id();

$positions = array_map(fn($c) => $c->getTreePos(), $categories);
$minTreePos = min($positions);
$maxTreePos = max($positions);

foreach ($categories as $category) {
$categoryId = $category->getId();
$code = $category->getCode();
$editUrl = $mainUrl.'&id='.$categoryId.'&action=edit';
$moveUrl = $mainUrl.'&id='.$categoryId.'&action=moveUp&tree_pos='.$category->getTreePos();
$deleteUrl = $mainUrl.'&id='.$categoryId.'&action=delete';
$exportUrl = $mainUrl.'&id='.$categoryId.'&action=export';
$treePos = $category->getTreePos();
$editUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [
'action' => 'edit',
'id' => $categoryId,
]));

$moveUpUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [
'action' => 'moveUp',
'id' => $categoryId,
'tree_pos' => $treePos,
]));

$moveDownUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [
'action' => 'moveDown',
'id' => $categoryId,
'tree_pos' => $treePos,
]));

$deleteUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [
'action' => 'delete',
'id' => $categoryId,
]));

$exportUrl = $baseUrl.'?'.http_build_query(array_merge($baseParams, [
'action' => 'export',
'id' => $categoryId,
]));

$actions = [];

Expand All @@ -410,7 +472,25 @@ function ($entry) use ($urlId) {

if ($inUrl->count() > 0) {
$actions[] = Display::url($editIcon, $editUrl);
$actions[] = Display::url($moveIcon, $moveUrl);

if ($treePos > $minTreePos) {
$actions[] = Display::url(
Display::getMdiIcon(ActionIcon::UP, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Move up')),
$moveUpUrl
);
} else {
$actions[] = Display::getMdiIcon(ActionIcon::UP, 'ch-tool-icon-disabled', null, ICON_SIZE_SMALL, get_lang('Move up'));
}

if ($treePos < $maxTreePos) {
$actions[] = Display::url(
Display::getMdiIcon(ActionIcon::DOWN, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Move down')),
$moveDownUrl
);
} else {
$actions[] = Display::getMdiIcon(ActionIcon::DOWN, 'ch-tool-icon-disabled', null, ICON_SIZE_SMALL, get_lang('Move down'));
}

$actions[] = Display::url($exportIcon, $exportUrl);
$actions[] = Display::url(
$deleteIcon,
Expand Down
Loading