diff --git a/public/main/admin/user_add.php b/public/main/admin/user_add.php
index 00814e80f4c..6c14b1d81fb 100644
--- a/public/main/admin/user_add.php
+++ b/public/main/admin/user_add.php
@@ -244,18 +244,23 @@ function updateStatus(){
$status[STUDENT_BOSS] = get_lang('Student\'s superior');
$status[INVITEE] = get_lang('Invitee');
-$form->addSelect(
- 'status',
- get_lang('Profile'),
- $status,
+$form->addElement(
+ 'select',
+ 'roles',
+ get_lang('Roles'),
+ api_get_roles(),
[
- 'id' => 'status_select',
- 'onchange' => 'javascript: updateStatus();',
+ 'multiple' => 'multiple',
+ 'size' => 8,
]
);
+$form->addRule('roles', get_lang('Required field'), 'required');
//drh list (display only if student)
-$display = (isset($_POST['status']) && STUDENT == $_POST['status']) || !isset($_POST['status']) ? 'block' : 'none';
+$display = 'none';
+if (isset($_POST['roles']) && is_array($_POST['roles'])) {
+ $display = in_array('ROLE_TEACHER', $_POST['roles']) || in_array('ROLE_SESSION_MANAGER', $_POST['roles']) ? 'block' : 'none';
+}
if (api_is_platform_admin()) {
// Platform admin
@@ -354,7 +359,6 @@ function updateStatus(){
$email = $user['email'];
$phone = $user['phone'];
$username = 'true' !== api_get_setting('login_is_email') ? $user['username'] : '';
- $status = (int) $user['status'];
$language = $user['locale'];
$picture = $_FILES['picture'];
$platform_admin = 0;
@@ -398,6 +402,12 @@ function updateStatus(){
$template = isset($user['email_template_option']) ? $user['email_template_option'] : [];
+ $roles = $user['roles'] ?? [];
+ $status = api_status_from_roles($roles);
+ if ((int) ($user['admin']['platform_admin'] ?? 0) === 1) {
+ $status = COURSEMANAGER;
+ }
+
$user_id = UserManager::create_user(
$firstname,
$lastname,
@@ -456,6 +466,14 @@ function updateStatus(){
);
}
+ $repo = Container::getUserRepository();
+ $userEntity = $repo->find($user_id);
+
+ if ($userEntity) {
+ $userEntity->setRoles($roles);
+ $repo->updateUser($userEntity);
+ }
+
$extraFieldValues = new ExtraFieldValue('user');
$user['item_id'] = $user_id;
$extraFieldValues->saveFieldValues($user);
diff --git a/public/main/admin/user_edit.php b/public/main/admin/user_edit.php
index 9ac69e7602f..07ec6639bec 100644
--- a/public/main/admin/user_edit.php
+++ b/public/main/admin/user_edit.php
@@ -250,26 +250,22 @@ function confirmation(name) {
$form->addGroup($group, 'password', null, null, false);
$form->addPasswordRule('password', 'password');
-// Status
-$status = [];
-$status[COURSEMANAGER] = get_lang('Trainer');
-$status[STUDENT] = get_lang('Learner');
-$status[DRH] = get_lang('Human Resources Manager');
-$status[SESSIONADMIN] = get_lang('Sessions administrator');
-$status[STUDENT_BOSS] = get_lang('Student\'s superior');
-$status[INVITEE] = get_lang('Invitee');
-
-$form->addSelect(
- 'status',
- get_lang('Profile'),
- $status,
+$form->addElement(
+ 'select',
+ 'roles',
+ get_lang('Roles'),
+ api_get_roles(),
[
- 'id' => 'status_select',
- 'onchange' => 'javascript: display_drh_list();',
+ 'multiple' => 'multiple',
+ 'size' => 8,
]
);
+$form->addRule('roles', get_lang('Required field'), 'required');
-$display = isset($user_data['status']) && (STUDENT == $user_data['status'] || (isset($_POST['status']) && STUDENT == $_POST['status'])) ? 'block' : 'none';
+$display = 'none';
+if (isset($_POST['roles']) && is_array($_POST['roles'])) {
+ $display = in_array('ROLE_TEACHER', $_POST['roles']) || in_array('ROLE_SESSION_MANAGER', $_POST['roles']) ? 'block' : 'none';
+}
// Platform admin
if (api_is_platform_admin()) {
@@ -402,6 +398,9 @@ function confirmation(name) {
$user_data['expiration_date'] = api_get_local_time($expiration_date);
}
}
+$availableRoles = array_keys(api_get_roles());
+$userRoles = array_intersect($userObj->getRoles(), $availableRoles);
+$user_data['roles'] = $userRoles;
$form->setDefaults($user_data);
$error_drh = false;
@@ -415,9 +414,9 @@ function confirmation(name) {
exit();
}
- $is_user_subscribed_in_course = CourseManager::is_user_subscribed_in_course($user['user_id']);
-
- if (DRH == $user['status'] && $is_user_subscribed_in_course) {
+ $roles = $user['roles'] ?? [];
+ $newStatus = api_status_from_roles($roles);
+ if ($newStatus === DRH && CourseManager::is_user_subscribed_in_course((int) $user_id)) {
$error_drh = true;
} else {
$picture_element = $form->getElement('picture');
@@ -445,7 +444,6 @@ function confirmation(name) {
$email = $user['email'];
$phone = $user['phone'];
$username = $user['username'] ?? $userInfo['username'];
- $status = (int) $user['status'];
$platform_admin = 0;
// Only platform admin can change user status to admin.
if (api_is_platform_admin()) {
@@ -462,23 +460,21 @@ function confirmation(name) {
}
$active = isset($user['active']) ? (int) $user['active'] : USER_SOFT_DELETED;
- //If the user is set to admin the status will be overwrite by COURSEMANAGER = 1
- if (1 == $platform_admin) {
- $status = COURSEMANAGER;
- }
-
if ('true' === api_get_setting('login_is_email')) {
$username = $email;
}
$template = $user['email_template_option'] ?? [];
+ if ((int) ($user['platform_admin'] ?? 0) === 1) {
+ $newStatus = COURSEMANAGER;
+ }
$incompatible = false;
$conflicts = [];
- $oldStatus = $userObj->getStatus();
- $newStatus = (int) $user['status'];
+ $oldStatus = (int) $userObj->getStatus();
+
if ($oldStatus !== $newStatus) {
- $isNowStudent = $newStatus === STUDENT;
+ $isNowStudent = ($newStatus === STUDENT);
if ($isNowStudent) {
$courseTeacherCount = $userObj->getCourses()->count();
$coachSessions = $userObj->getSessionsAsGeneralCoach();
@@ -525,7 +521,7 @@ function confirmation(name) {
$password,
$auth_source,
$email,
- $status,
+ $newStatus,
$official_code,
$phone,
$picture_uri,
@@ -542,7 +538,7 @@ function confirmation(name) {
$template
);
- $studentBossListSent = isset($user['student_boss']) ? $user['student_boss'] : [];
+ $studentBossListSent = $user['student_boss'] ?? [];
UserManager::subscribeUserToBossList(
$user_id,
$studentBossListSent,
@@ -559,13 +555,20 @@ function confirmation(name) {
}
}
+ $repo = Container::getUserRepository();
+ $userEntity = $repo->find($user_id);
+ if ($userEntity) {
+ $userEntity->setRoles($roles);
+ $repo->updateUser($userEntity);
+ }
+
$extraFieldValue = new ExtraFieldValue('user');
$extraFieldValue->saveFieldValues($user);
$userInfo = api_get_user_info($user_id);
$message = get_lang('User updated').': '.Display::url(
- $userInfo['complete_name_with_username'],
- api_get_path(WEB_CODE_PATH).'admin/user_edit.php?user_id='.$user_id
- );
+ $userInfo['complete_name_with_username'],
+ api_get_path(WEB_CODE_PATH).'admin/user_edit.php?user_id='.$user_id
+ );
Session::erase('system_timezone');
diff --git a/public/main/admin/user_list.php b/public/main/admin/user_list.php
index 0a30ba833df..52f452400af 100644
--- a/public/main/admin/user_list.php
+++ b/public/main/admin/user_list.php
@@ -4,6 +4,7 @@
use Chamilo\CoreBundle\Entity\User;
use Chamilo\CoreBundle\Enums\StateIcon;
+use Chamilo\CoreBundle\Framework\Container;
use ChamiloSession as Session;
/**
@@ -165,7 +166,7 @@ function trimVariables()
function prepare_user_sql_query(bool $getCount, bool $showDeletedUsers = false): string
{
$sql = '';
- $user_table = Database::get_main_table(TABLE_MAIN_USER);
+ $user_table = Database::get_main_table(TABLE_MAIN_USER);
$admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
$isMultipleUrl = (api_is_platform_admin() || api_is_session_admin()) && api_get_multiple_access_url();
@@ -174,7 +175,9 @@ function prepare_user_sql_query(bool $getCount, bool $showDeletedUsers = false):
if ($getCount) {
$sql .= "SELECT COUNT(u.id) AS total_number_of_items FROM $user_table u";
} else {
- $sql .= 'SELECT u.id AS col0, u.official_code AS col2, ';
+ $sql .= 'SELECT
+ u.id AS col0,
+ u.official_code AS col2, ';
if (api_is_western_name_order()) {
$sql .= 'u.firstname AS col3, u.lastname AS col4, ';
@@ -183,26 +186,24 @@ function prepare_user_sql_query(bool $getCount, bool $showDeletedUsers = false):
}
$sql .= " u.username AS col5,
- u.email AS col6,
- u.status AS col7,
- u.active AS col8,
- u.created_at AS col9,
- u.last_login as col10,
- u.id AS col11,
- u.expiration_date AS exp,
- u.password
- FROM $user_table u";
+ u.email AS col6,
+ u.status AS col7,
+ u.active AS col8,
+ u.created_at AS col9,
+ u.last_login AS col10,
+ u.id AS col11,
+ u.expiration_date AS exp,
+ u.password
+ FROM $user_table u";
}
- // adding the filter to see the user's only of the current access_url
if ($isMultipleUrl) {
$access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
$sql .= " INNER JOIN $access_url_rel_user_table url_rel_user
- ON (u.id=url_rel_user.user_id)";
+ ON (u.id = url_rel_user.user_id)";
}
$classId = isset($_REQUEST['class_id']) && !empty($_REQUEST['class_id']) ? (int) $_REQUEST['class_id'] : 0;
-
if ($classId) {
$userGroupTable = Database::get_main_table(TABLE_USERGROUP_REL_USER);
$sql .= " INNER JOIN $userGroupTable ug ON (ug.user_id = u.id)";
@@ -214,112 +215,145 @@ function prepare_user_sql_query(bool $getCount, bool $showDeletedUsers = false):
'keyword_username',
'keyword_email',
'keyword_officialcode',
- 'keyword_status',
+ 'keyword_roles',
'keyword_active',
'keyword_inactive',
'check_easy_passwords',
];
-
$keywordListValues = [];
$atLeastOne = false;
foreach ($keywordList as $keyword) {
$keywordListValues[$keyword] = null;
- if (isset($_GET[$keyword]) && !empty($_GET[$keyword])) {
+ if (isset($_GET[$keyword]) && $_GET[$keyword] !== '') {
$keywordListValues[$keyword] = Security::remove_XSS($_GET[$keyword]);
$atLeastOne = true;
}
}
-
- if (false == $atLeastOne) {
+ if (!$atLeastOne) {
$keywordListValues = [];
}
- if (isset($_GET['keyword']) && !empty($_GET['keyword'])) {
- $keywordFiltered = Database::escape_string("%".$_GET['keyword']."%");
- $sql .= " WHERE (
- u.firstname LIKE '$keywordFiltered' OR
- u.lastname LIKE '$keywordFiltered' OR
- concat(u.firstname, ' ', u.lastname) LIKE '$keywordFiltered' OR
- concat(u.lastname,' ',u.firstname) LIKE '$keywordFiltered' OR
- u.username LIKE '$keywordFiltered' OR
- u.official_code LIKE '$keywordFiltered' OR
- u.email LIKE '$keywordFiltered'
- )
- ";
- } elseif (isset($keywordListValues) && !empty($keywordListValues)) {
- $query_admin_table = '';
- $keyword_admin = '';
-
- if (isset($keywordListValues['keyword_status']) &&
- PLATFORM_ADMIN == $keywordListValues['keyword_status']
- ) {
- $query_admin_table = " , $admin_table a ";
- $keyword_admin = ' AND a.user_id = u.id ';
- $keywordListValues['keyword_status'] = '';
- }
+ $roles = [];
+ if (!empty($keywordListValues['keyword_roles'])) {
+ $raw = $keywordListValues['keyword_roles'];
+ $roles = is_array($raw) ? $raw : array_filter(array_map('trim', explode(',', (string) $raw)));
+ $available = array_keys(api_get_roles());
+ if (empty($available)) { $available = array_values(api_get_roles()); }
+ $roles = array_values(array_unique(array_intersect($roles, $available)));
+ }
- if ('%' === $keywordListValues['keyword_status']) {
- $keywordListValues['keyword_status'] = '';
+ $roleToStatus = [
+ 'ROLE_TEACHER' => COURSEMANAGER,
+ 'TEACHER' => COURSEMANAGER,
+ 'ROLE_STUDENT' => STUDENT,
+ 'STUDENT' => STUDENT,
+ 'ROLE_HR' => DRH,
+ 'HR' => DRH,
+ 'ROLE_SESSION_MANAGER' => SESSIONADMIN,
+ 'SESSION_MANAGER' => SESSIONADMIN,
+ 'ROLE_STUDENT_BOSS' => STUDENT_BOSS,
+ 'STUDENT_BOSS' => STUDENT_BOSS,
+ 'ROLE_INVITEE' => INVITEE,
+ 'INVITEE' => INVITEE,
+ ];
+ $mappedStatuses = [];
+ foreach ($roles as $r) {
+ if (isset($roleToStatus[$r])) {
+ $mappedStatuses[] = (int) $roleToStatus[$r];
}
+ }
+ $mappedStatuses = array_values(array_unique($mappedStatuses));
+
+ $adminVariants = ['ROLE_PLATFORM_ADMIN','PLATFORM_ADMIN','ROLE_SUPER_ADMIN','SUPER_ADMIN','ROLE_GLOBAL_ADMIN','GLOBAL_ADMIN','ROLE_ADMIN','ADMIN'];
+ $needsAdminLeftJoin = (bool) array_intersect($roles, $adminVariants);
+ if ($needsAdminLeftJoin) {
+ $sql .= " LEFT JOIN $admin_table a ON (a.user_id = u.id) ";
+ }
- $keyword_extra_value = '';
- $sql .= " $query_admin_table
- WHERE ( 1 = 1 ";
+ if (isset($_GET['keyword']) && $_GET['keyword'] !== '') {
+ $keywordFiltered = Database::escape_string("%".$_GET['keyword']."%");
+ $sql .= " WHERE (
+ u.firstname LIKE '$keywordFiltered' OR
+ u.lastname LIKE '$keywordFiltered' OR
+ concat(u.firstname, ' ', u.lastname) LIKE '$keywordFiltered' OR
+ concat(u.lastname, ' ', u.firstname) LIKE '$keywordFiltered' OR
+ u.username LIKE '$keywordFiltered' OR
+ u.official_code LIKE '$keywordFiltered' OR
+ u.email LIKE '$keywordFiltered'
+ )";
+ } elseif (!empty($keywordListValues)) {
+ $sql .= " WHERE (1 = 1 ";
if (!empty($keywordListValues['keyword_firstname'])) {
- $sql .= "AND u.firstname LIKE '".Database::escape_string("%".$keywordListValues['keyword_firstname']."%")."'";
+ $sql .= " AND u.firstname LIKE '".Database::escape_string("%".$keywordListValues['keyword_firstname']."%")."'";
}
- // This block is never executed because $keyword_extra_data never exists
if (!empty($keywordListValues['keyword_lastname'])) {
- $sql .= "AND u.lastname LIKE '".Database::escape_string("%".$keywordListValues['keyword_lastname']."%")."'";
+ $sql .= " AND u.lastname LIKE '".Database::escape_string("%".$keywordListValues['keyword_lastname']."%")."'";
}
if (!empty($keywordListValues['keyword_username'])) {
- $sql .= "AND u.username LIKE '".Database::escape_string("%".$keywordListValues['keyword_username']."%")."'";
+ $sql .= " AND u.username LIKE '".Database::escape_string("%".$keywordListValues['keyword_username']."%")."'";
}
if (!empty($keywordListValues['keyword_email'])) {
- $sql .= "AND u.email LIKE '".Database::escape_string("%".$keywordListValues['keyword_email']."%")."'";
+ $sql .= " AND u.email LIKE '".Database::escape_string("%".$keywordListValues['keyword_email']."%")."'";
}
-
- if (!empty($keywordListValues['keyword_status'])) {
- $sql .= "AND u.status = '".Database::escape_string($keywordListValues['keyword_status'])."'";
+ if (!empty($keywordListValues['keyword_officialcode'])) {
+ $sql .= " AND u.official_code LIKE '".Database::escape_string("%".$keywordListValues['keyword_officialcode']."%")."'";
}
- if (!empty($keywordListValues['keyword_officialcode'])) {
- $sql .= " AND u.official_code LIKE '".Database::escape_string("%".$keywordListValues['keyword_officialcode']."%")."' ";
+ if (!empty($roles)) {
+ $roleConds = [];
+
+ foreach ($roles as $role) {
+ $u = strtoupper($role);
+ $variants = [$u];
+ if (strpos($u, 'ROLE_') === 0) {
+ $variants[] = substr($u, 5);
+ } else {
+ $variants[] = 'ROLE_'.$u;
+ }
+ $variants = array_values(array_unique($variants));
+
+ $likes = [];
+ foreach ($variants as $v) {
+ $esc = Database::escape_string($v);
+ $likes[] = "u.roles LIKE '%\"$esc\"%'";
+ }
+ $roleConds[] = '('.implode(' OR ', $likes).')';
+ }
+
+ if (!empty($mappedStatuses)) {
+ $roleConds[] = 'u.status IN ('.implode(',', array_map('intval', $mappedStatuses)).')';
+ }
+
+ if ($needsAdminLeftJoin) {
+ $roleConds[] = 'a.user_id IS NOT NULL';
+ }
+
+ $sql .= ' AND ('.implode(' OR ', $roleConds).')';
}
- $sql .= " $keyword_admin $keyword_extra_value ";
- if (isset($keywordListValues['keyword_active']) &&
- !isset($keywordListValues['keyword_inactive'])
- ) {
+ if (isset($keywordListValues['keyword_active']) && !isset($keywordListValues['keyword_inactive'])) {
$sql .= ' AND u.active = 1';
- } elseif (isset($keywordListValues['keyword_inactive']) &&
- !isset($keywordListValues['keyword_active'])
- ) {
+ } elseif (isset($keywordListValues['keyword_inactive']) && !isset($keywordListValues['keyword_active'])) {
$sql .= ' AND u.active = 0';
}
- $sql .= ' ) ';
+
+ $sql .= ' )';
}
if ($classId) {
$sql .= " AND ug.usergroup_id = $classId";
}
- $preventSessionAdminsToManageAllUsers = api_get_setting('prevent_session_admins_to_manage_all_users');
-
- $extraConditions = '';
- if (api_is_session_admin() && 'true' === $preventSessionAdminsToManageAllUsers) {
- $extraConditions .= ' AND u.creator_id = '.api_get_user_id();
+ if (api_is_session_admin() && api_get_setting('prevent_session_admins_to_manage_all_users') === 'true') {
+ $sql .= ' AND u.creator_id = '.api_get_user_id();
}
- // adding the filter to see the user's only of the current access_url
if ($isMultipleUrl) {
- $extraConditions .= ' AND url_rel_user.access_url_id = '.$urlId;
+ $sql .= ' AND url_rel_user.access_url_id = '.$urlId;
}
- $sql .= $extraConditions;
-
$variables = Session::read('variables_to_show', []);
$extraFields = api_get_setting('profile.user_search_on_extra_fields', true);
@@ -332,33 +366,32 @@ function prepare_user_sql_query(bool $getCount, bool $showDeletedUsers = false):
}
$variables = array_merge($extraFieldList, $variables);
}
+
if (!empty($variables)) {
$extraField = new ExtraField('user');
$extraFieldResult = [];
$extraFieldHasData = [];
+
foreach ($variables as $variable) {
if (isset($_GET['extra_'.$variable])) {
- if (is_array($_GET['extra_'.$variable])) {
- $values = $_GET['extra_'.$variable];
- } else {
- $values = [$_GET['extra_'.$variable]];
- }
+ $values = is_array($_GET['extra_'.$variable])
+ ? $_GET['extra_'.$variable]
+ : [$_GET['extra_'.$variable]];
if (empty($values)) {
continue;
}
$info = $extraField->get_handler_field_info_by_field_variable($variable);
-
if (empty($info)) {
continue;
}
foreach ($values as $value) {
- if (empty($value)) {
+ if ($value === '' || $value === null) {
continue;
}
- if (ExtraField::FIELD_TYPE_TAG == $info['value_type']) {
+ if (ExtraField::FIELD_TYPE_TAG === $info['value_type']) {
$result = $extraField->getAllUserPerTag($info['id'], $value);
$result = empty($result) ? [] : array_column($result, 'user_id');
} else {
@@ -373,22 +406,18 @@ function prepare_user_sql_query(bool $getCount, bool $showDeletedUsers = false):
}
}
- $condition = ' AND ';
- // If simple search then use "OR"
- if (isset($_GET['keyword']) && !empty($_GET['keyword'])) {
- $condition = ' OR ';
- }
-
+ $condition = isset($_GET['keyword']) ? ' OR ' : ' AND ';
if (!empty($extraFieldHasData) && !empty($extraFieldResult)) {
- $sql .= " $condition (u.id IN ('".implode("','", $extraFieldResult)."') $extraConditions ) ";
+ $sql .= " $condition u.id IN ('".implode("','", $extraFieldResult)."')";
}
}
if ($showDeletedUsers) {
- $sql .= !str_contains($sql, 'WHERE') ? ' WHERE u.active = '.USER_SOFT_DELETED : ' AND u.active = '.USER_SOFT_DELETED;
+ $sql .= (!str_contains($sql, 'WHERE') ? ' WHERE ' : ' AND ').'u.active = '.USER_SOFT_DELETED;
} else {
- $sql .= !str_contains($sql, 'WHERE') ? ' WHERE u.active <> '.USER_SOFT_DELETED : ' AND u.active <> '.USER_SOFT_DELETED;
+ $sql .= (!str_contains($sql, 'WHERE') ? ' WHERE ' : ' AND ').'u.active <> '.USER_SOFT_DELETED;
}
+
$sql .= ' AND u.status <> '.User::ROLE_FALLBACK;
return $sql;
@@ -419,9 +448,6 @@ function get_user_data(int $from, int $number_of_items, int $column, string $dir
if (!in_array($direction, ['ASC', 'DESC'])) {
$direction = 'ASC';
}
- $column = (int) $column;
- $from = (int) $from;
- $number_of_items = (int) $number_of_items;
$sql .= " ORDER BY col$column $direction ";
$sql .= " LIMIT $from, $number_of_items";
@@ -458,7 +484,7 @@ function get_user_data(int $from, int $number_of_items, int $column, string $dir
$user[3],
$user[4], // username
$user[5], // email
- $user[6],
+ $user[0],
$user[7], // active
api_get_local_time($user[8]),
api_get_local_time($user[9], null, null, true),
@@ -568,15 +594,15 @@ function modify_filter($user_id, $url_params, $row): string
{
$_admins_list = Session::read('admin_list', []);
$is_admin = in_array($user_id, $_admins_list);
- $statusname = api_get_status_langvars();
+
+ $repo = Container::getUserRepository();
+ $userEntity = $repo->find($user_id);
+ $userRoles = $userEntity ? $userEntity->getRoles() : [];
+
$currentUserId = api_get_user_id();
- $user_is_anonymous = false;
+ $user_is_anonymous = in_array('ROLE_ANONYMOUS', $userRoles, true);
$current_user_status_label = $row['7'];
-
- if ($current_user_status_label == $statusname[ANONYMOUS]) {
- $user_is_anonymous = true;
- }
$result = '';
if (api_is_platform_admin()) {
@@ -589,16 +615,21 @@ function modify_filter($user_id, $url_params, $row): string
}
}
- // Only allow platform admins to login_as, or session admins only for students (not teachers nor other admins)
- $loginAsStatusForSessionAdmins = [$statusname[STUDENT]];
+ $loginAsRolesForSessionAdmins = ['ROLE_STUDENT'];
- // Except when session.allow_session_admin_login_as_teacher is enabled, then can login_as teachers also
if ('true' === api_get_setting('session.allow_session_admin_login_as_teacher')) {
- $loginAsStatusForSessionAdmins[] = $statusname[COURSEMANAGER];
+ $loginAsRolesForSessionAdmins[] = 'ROLE_TEACHER';
}
- $sessionAdminCanLoginAs = api_is_session_admin() &&
- in_array($current_user_status_label, $loginAsStatusForSessionAdmins);
+ $sessionAdminCanLoginAs = false;
+ if (api_is_session_admin()) {
+ foreach ($loginAsRolesForSessionAdmins as $role) {
+ if (in_array($role, $userRoles, true)) {
+ $sessionAdminCanLoginAs = true;
+ break;
+ }
+ }
+ }
if (api_is_platform_admin() || $sessionAdminCanLoginAs) {
if (!$user_is_anonymous) {
@@ -615,7 +646,7 @@ function modify_filter($user_id, $url_params, $row): string
$result .= Display::getMdiIcon('account-key', 'ch-tool-icon-disabled', null, 22, get_lang('Login as'));
}
- if ($current_user_status_label != $statusname[STUDENT]) {
+ if (!in_array('ROLE_STUDENT', $userRoles, true)) {
$result .= Display::getMdiIcon(
'chart-box',
'ch-tool-icon-disabled',
@@ -650,12 +681,12 @@ function modify_filter($user_id, $url_params, $row): string
'';
} else {
$result .= Display::getMdiIcon(
- 'pencil',
- 'ch-tool-icon-disabled',
- null,
- 22,
- get_lang('Edit')
- ).'';
+ 'pencil',
+ 'ch-tool-icon-disabled',
+ null,
+ 22,
+ get_lang('Edit')
+ ).'';
}
}
@@ -807,7 +838,12 @@ function modify_filter($user_id, $url_params, $row): string
// actions for assigning sessions, courses or users
if (!api_is_session_admin()) {
- if ($current_user_status_label == $statusname[SESSIONADMIN]) {
+ $isSessionManager = in_array('ROLE_SESSION_MANAGER', $userRoles, true);
+ $isHR = in_array('ROLE_HR', $userRoles, true);
+ $isStudentBoss = in_array('ROLE_STUDENT_BOSS', $userRoles, true);
+ $isAdmin = UserManager::is_admin($user_id);
+
+ if ($isSessionManager) {
$result .= Display::url(
Display::getMdiIcon(
'google-classroom',
@@ -819,10 +855,7 @@ function modify_filter($user_id, $url_params, $row): string
"dashboard_add_sessions_to_user.php?user={$user_id}"
);
} else {
- if ($current_user_status_label == $statusname[DRH] ||
- UserManager::is_admin($user_id) ||
- $current_user_status_label == $statusname[STUDENT_BOSS]
- ) {
+ if ($isHR || $isAdmin || $isStudentBoss) {
$result .= Display::url(
Display::getMdiIcon(
'account-child',
@@ -836,7 +869,7 @@ function modify_filter($user_id, $url_params, $row): string
);
}
- if ($current_user_status_label == $statusname[DRH] || UserManager::is_admin($user_id)) {
+ if ($isHR || $isAdmin) {
$result .= Display::url(
Display::getMdiIcon(
'book-open-page-variant',
@@ -916,15 +949,40 @@ function active_filter(int $active, string $params, array $row): string
}
/**
- * Instead of displaying the integer of the status, we give a translation for the status.
+ * Returns a list of user roles excluding the default ROLE_USER.
+ *
+ * @param int $userId The user ID.
+ * @return string HTML string with roles separated by
.
*/
-function status_filter(int $status): string
+function roles_filter($userId): string
{
- $name = api_get_status_langvars();
+ static $map = null;
+ if ($map === null) {
+ $map = api_get_roles();
+ }
+
+ $repo = Container::getUserRepository();
+ $user = $repo->find($userId);
+ if (!$user) {
+ return '';
+ }
+
+ $codes = array_filter($user->getRoles(), static function ($code) {
+ $u = strtoupper($code);
+ return $u !== 'ROLE_USER' && $u !== 'USER' && $u !== 'ROLE_ANONYMOUS' && $u !== 'ANONYMOUS';
+ });
- return $name[$status];
+ $labels = array_map(static function ($code) use ($map) {
+ $u = strtoupper($code);
+ $bare = preg_replace('/^ROLE_/', '', $u);
+ $label = $map[$u] ?? $map[$bare] ?? ucwords(strtolower(str_replace('_', ' ', $bare)));
+ return htmlentities($label);
+ }, $codes);
+
+ return implode('
', $labels);
}
+
if (isset($_GET['keyword']) || isset($_GET['keyword_firstname'])) {
$interbreadcrumb[] = ['url' => 'index.php', 'name' => get_lang('Administration')];
$interbreadcrumb[] = ['url' => 'user_list.php', 'name' => get_lang('User list')];
@@ -1169,7 +1227,7 @@ class="btn btn--plain advanced_options" onclick="display_advanced_search_form();
$actionsRight = '';
if (api_is_platform_admin() && !$showDeletedUsers) {
$actionsLeft .= ''.
- Display::getMdiIcon('account-plus', 'ch-tool-icon-gradient', null, 32, get_lang('Add a user')).'';
+ Display::getMdiIcon('account-plus', 'ch-tool-icon-gradient', null, 32, get_lang('Add a user')).'';
}
$actionsRight .= $form->returnForm();
@@ -1183,7 +1241,10 @@ class="btn btn--plain advanced_options" onclick="display_advanced_search_form();
$parameters['keyword_username'] = Security::remove_XSS($_GET['keyword_username']);
$parameters['keyword_email'] = Security::remove_XSS($_GET['keyword_email']);
$parameters['keyword_officialcode'] = Security::remove_XSS($_GET['keyword_officialcode']);
- $parameters['keyword_status'] = Security::remove_XSS($_GET['keyword_status']);
+ if (isset($_GET['keyword_roles'])) {
+ $keywordRoles = is_array($_GET['keyword_roles']) ? $_GET['keyword_roles'] : [$_GET['keyword_roles']];
+ $parameters['keyword_roles'] = implode(',', array_map('Security::remove_XSS', $keywordRoles));
+ }
if (isset($_GET['keyword_active'])) {
$parameters['keyword_active'] = Security::remove_XSS($_GET['keyword_active']);
}
@@ -1230,19 +1291,11 @@ class="btn btn--plain advanced_options" onclick="display_advanced_search_form();
['url' => api_get_path(WEB_AJAX_PATH).'usergroup.ajax.php?a=get_class_by_keyword']
);
-$status_options = [];
-$status_options['%'] = get_lang('All');
-$status_options[STUDENT] = get_lang('Learner');
-$status_options[COURSEMANAGER] = get_lang('Trainer');
-$status_options[DRH] = get_lang('Human Resources Manager');
-$status_options[SESSIONADMIN] = get_lang('Sessions administrator');
-$status_options[PLATFORM_ADMIN] = get_lang('Administrator');
-$status_options[STUDENT_BOSS] = get_lang("Student's superior");
-
$form->addSelect(
- 'keyword_status',
- get_lang('Profile'),
- $status_options
+ 'keyword_roles',
+ get_lang('Roles'),
+ api_get_roles(),
+ ['multiple' => true, 'size' => 6]
);
$active_group = [];
@@ -1291,7 +1344,7 @@ function($from, $number_of_items, $column, $direction) use ($showDeletedUsers) {
}
$table->set_header(5, get_lang('Username'));
$table->set_header(6, get_lang('E-mail'));
-$table->set_header(7, get_lang('Profile'));
+$table->set_header(7, get_lang('Roles'));
$table->set_header(8, get_lang('active'), true, 'width="15px"');
$table->set_header(9, get_lang('Registration date'), true, 'width="90px"');
$table->set_header(10, get_lang('Latest login'), true, 'width="90px"');
@@ -1300,7 +1353,7 @@ function($from, $number_of_items, $column, $direction) use ($showDeletedUsers) {
$table->set_column_filter(3, 'user_filter');
$table->set_column_filter(4, 'user_filter');
$table->set_column_filter(6, 'email_filter');
-$table->set_column_filter(7, 'status_filter');
+$table->set_column_filter(7, 'roles_filter');
$table->set_column_filter(8, 'active_filter');
$actionsList = [];
if ($showDeletedUsers) {
diff --git a/public/main/inc/lib/api.lib.php b/public/main/inc/lib/api.lib.php
index 3845ef11d9f..38c0495e42c 100644
--- a/public/main/inc/lib/api.lib.php
+++ b/public/main/inc/lib/api.lib.php
@@ -3030,21 +3030,12 @@ function api_is_course_session_coach($user_id, $courseId, $session_id)
/**
* Checks whether the current user is a course or session coach.
- *
- * @param int $session_id
- * @param int $courseId
- * @param bool Check whether we are in student view and, if we are, return false
- * @param int $userId
- *
- * @return bool True if current user is a course or session coach
*/
-function api_is_coach($session_id = 0, $courseId = null, $check_student_view = true, $userId = 0)
+function api_is_coach(int $session_id = 0, ?int $courseId = null, bool $check_student_view = true, int $userId = 0): bool
{
- $userId = empty($userId) ? api_get_user_id() : (int) $userId;
+ $userId = empty($userId) ? api_get_user_id() : $userId;
- if (!empty($session_id)) {
- $session_id = (int) $session_id;
- } else {
+ if (empty($session_id)) {
$session_id = api_get_session_id();
}
@@ -3053,9 +3044,7 @@ function api_is_coach($session_id = 0, $courseId = null, $check_student_view = t
return false;
}
- if (!empty($courseId)) {
- $courseId = (int) $courseId;
- } else {
+ if (empty($courseId)) {
$courseId = api_get_course_int_id();
}
@@ -3314,110 +3303,148 @@ function api_display_tool_view_option()
}
/**
- * Function that removes the need to directly use is_courseAdmin global in
- * tool scripts. It returns true or false depending on the user's rights in
- * this particular course.
- * Optionally checking for tutor and coach roles here allows us to use the
- * student_view feature altogether with these roles as well.
- *
- * @param bool Whether to check if the user has the tutor role
- * @param bool Whether to check if the user has the coach role
- * @param bool Whether to check if the user has the session coach role
- * @param bool check the student view or not
+ * Determines whether the current user is allowed to edit the current context.
*
- * @author Roan Embrechts
- * @author Patrick Cool
- * @author Julio Montoya
+ * This includes checks for platform admin, course admin, tutor, coach,
+ * session coach, and optionally verifies if the user is in student view mode.
+ * If not in a course context, it falls back to a role-based permission system.
*
- * @version 1.1, February 2004
+ * @param bool $tutor Allow if the user is a tutor.
+ * @param bool $coach Allow if the user is a coach and setting allows it.
+ * @param bool $session_coach Allow if the user is a session coach.
+ * @param bool $check_student_view Check if student view mode is active.
*
- * @return bool true: the user has the rights to edit, false: he does not
+ * @return bool True if the user is allowed to edit, false otherwise.
*/
function api_is_allowed_to_edit(
- $tutor = false,
- $coach = false,
- $session_coach = false,
- $check_student_view = true
-) {
+ bool $tutor = false,
+ bool $coach = false,
+ bool $session_coach = false,
+ bool $check_student_view = true
+): bool {
$allowSessionAdminEdit = 'true' === api_get_setting('session.session_admins_edit_courses_content');
- // Admins can edit anything.
+ $sessionId = api_get_session_id();
+ $sessionVisibility = api_get_session_visibility($sessionId);
+ $studentView = api_is_student_view_active();
+ $isAllowed = false;
+
+ // If platform admin, allow unless student view is active
if (api_is_platform_admin($allowSessionAdminEdit)) {
- //The student preview was on
- if ($check_student_view && api_is_student_view_active()) {
- return false;
+ if ($check_student_view && $studentView) {
+ $isAllowed = false;
+ } else {
+ return true;
}
-
- return true;
}
- $sessionId = api_get_session_id();
-
+ // Respect session course read-only mode from extra field
if ($sessionId && 'true' === api_get_setting('session.session_courses_read_only_mode')) {
$efv = new ExtraFieldValue('course');
- $lockExrafieldField = $efv->get_values_by_handler_and_field_variable(
+ $lock = $efv->get_values_by_handler_and_field_variable(
api_get_course_int_id(),
'session_courses_read_only_mode'
);
-
- if (!empty($lockExrafieldField['value'])) {
+ if (!empty($lock['value'])) {
return false;
}
}
- $is_allowed_coach_to_edit = api_is_coach(null, null, $check_student_view);
- $session_visibility = api_get_session_visibility($sessionId);
- $is_courseAdmin = api_is_course_admin();
+ $isCourseAdmin = api_is_course_admin();
+ $isCoach = api_is_coach(0, null, $check_student_view);
- if (!$is_courseAdmin && $tutor) {
- // If we also want to check if the user is a tutor...
- $is_courseAdmin = $is_courseAdmin || api_is_course_tutor();
+ if (!$isCourseAdmin && $tutor) {
+ $isCourseAdmin = api_is_course_tutor();
}
- if (!$is_courseAdmin && $coach) {
- // If we also want to check if the user is a coach...';
- // Check if session visibility is read only for coaches.
- if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
- $is_allowed_coach_to_edit = false;
+ if (!$isCourseAdmin && $coach) {
+ if (SESSION_VISIBLE_READ_ONLY == $sessionVisibility) {
+ $isCoach = false;
}
-
if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
- // Check if coach is allowed to edit a course.
- $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
+ $isCourseAdmin = $isCoach;
}
}
- if (!$is_courseAdmin && $session_coach) {
- $is_courseAdmin = $is_courseAdmin || $is_allowed_coach_to_edit;
+ if (!$isCourseAdmin && $session_coach) {
+ $isCourseAdmin = $isCoach;
}
- // Check if the student_view is enabled, and if so, if it is activated.
+ // Handle student view mode
if ('true' === api_get_setting('student_view_enabled')) {
- $studentView = api_is_student_view_active();
if (!empty($sessionId)) {
- // Check if session visibility is read only for coaches.
- if (SESSION_VISIBLE_READ_ONLY == $session_visibility) {
- $is_allowed_coach_to_edit = false;
+ if (SESSION_VISIBLE_READ_ONLY == $sessionVisibility) {
+ $isCoach = false;
}
-
- $is_allowed = false;
if ('true' === api_get_setting('allow_coach_to_edit_course_session')) {
- // Check if coach is allowed to edit a course.
- $is_allowed = $is_allowed_coach_to_edit;
+ $isAllowed = $isCoach;
}
+
if ($check_student_view) {
- $is_allowed = $is_allowed && false === $studentView;
+ $isAllowed = $isAllowed && !$studentView;
}
} else {
- $is_allowed = $is_courseAdmin;
+ $isAllowed = $isCourseAdmin;
if ($check_student_view) {
- $is_allowed = $is_courseAdmin && false === $studentView;
+ $isAllowed = $isCourseAdmin && !$studentView;
}
}
- return $is_allowed;
+ if ($isAllowed) {
+ return true;
+ }
} else {
- return $is_courseAdmin;
+ if ($isCourseAdmin) {
+ return true;
+ }
+ }
+
+ // Final fallback: permission-based system (only if nothing before returned true)
+ $courseId = api_get_course_id();
+ $inCourse = !empty($courseId) && $courseId != -1;
+
+ if (!$inCourse) {
+ $userRoles = api_get_user_roles();
+ $feature = api_detect_feature_context();
+ $permission = $feature.':edit';
+
+ return api_get_permission($permission, $userRoles);
}
+
+ return $isAllowed;
+}
+
+/**
+ * Returns the current main feature (module) based on the current script path.
+ * Used to determine permissions for non-course tools.
+ */
+function api_detect_feature_context(): string
+{
+ $script = $_SERVER['SCRIPT_NAME'] ?? '';
+ $script = basename($script);
+
+ $map = [
+ 'user_list.php' => 'user',
+ 'user_add.php' => 'user',
+ 'user_edit.php' => 'user',
+ 'session_list.php' => 'session',
+ 'session_add.php' => 'session',
+ 'session_edit.php' => 'session',
+ 'skill_list.php' => 'skill',
+ 'skill_edit.php' => 'skill',
+ 'badge_list.php' => 'badge',
+ 'settings.php' => 'settings',
+ 'course_list.php' => 'course',
+ ];
+
+ if (isset($map[$script])) {
+ return $map[$script];
+ }
+
+ if (preg_match('#/main/([a-z_]+)/#i', $_SERVER['SCRIPT_NAME'], $matches)) {
+ return $matches[1];
+ }
+
+ return 'unknown';
}
/**
@@ -6354,15 +6381,120 @@ function api_set_default_visibility(
}
}
-function api_get_roles()
+/**
+ * Returns available role codes => translated labels.
+ * Uses DI PermissionHelper and caches results.
+ */
+function api_get_roles(): array
+{
+ static $cache = null;
+ if ($cache !== null) {
+ return $cache;
+ }
+
+ $codes = Container::$container
+ ->get(\Chamilo\CoreBundle\Helpers\PermissionHelper::class)
+ ->getUserRoles(); // list of role codes from DB
+
+ // Built-in labels fallbacks. DB codes are used as keys.
+ $labels = [
+ 'ROLE_STUDENT' => get_lang('Learner'),
+ 'STUDENT' => get_lang('Learner'),
+ 'ROLE_TEACHER' => get_lang('Teacher'),
+ 'TEACHER' => get_lang('Teacher'),
+ 'ROLE_HR' => get_lang('Human Resources Manager'),
+ 'HR' => get_lang('Human Resources Manager'),
+ 'ROLE_SESSION_MANAGER' => get_lang('Session administrator'),
+ 'SESSION_MANAGER' => get_lang('Session administrator'),
+ 'ROLE_STUDENT_BOSS' => get_lang('Superior (n+1)'),
+ 'STUDENT_BOSS' => get_lang('Superior (n+1)'),
+ 'ROLE_INVITEE' => get_lang('Invitee'),
+ 'INVITEE' => get_lang('Invitee'),
+ 'ROLE_QUESTION_MANAGER' => get_lang('Question manager'),
+ 'QUESTION_MANAGER' => get_lang('Question manager'),
+ 'ROLE_ADMIN' => get_lang('Admin'),
+ 'ADMIN' => get_lang('Admin'),
+ 'ROLE_PLATFORM_ADMIN' => get_lang('Administrator'),
+ 'PLATFORM_ADMIN' => get_lang('Administrator'),
+ 'ROLE_SUPER_ADMIN' => get_lang('Super admin'),
+ 'SUPER_ADMIN' => get_lang('Super admin'),
+ 'ROLE_GLOBAL_ADMIN' => get_lang('Global admin'),
+ 'GLOBAL_ADMIN' => get_lang('Global admin'),
+ 'ROLE_ANONYMOUS' => 'Anonymous',
+ 'ANONYMOUS' => 'Anonymous',
+ 'ROLE_USER' => 'User',
+ 'USER' => 'User',
+ ];
+
+ $out = [];
+ foreach ((array) $codes as $code) {
+ $canon = strtoupper(trim((string) $code));
+ $label =
+ ($labels[$canon] ?? null) ??
+ ($labels['ROLE_'.$canon] ?? null) ??
+ ucwords(strtolower(str_replace('_', ' ', preg_replace('/^ROLE_/', '', $canon))));
+ $out[$code] = $label;
+ }
+
+ ksort($out, SORT_STRING);
+ return $cache = $out;
+}
+
+/**
+ * Normalizes a role code to canonical "ROLE_*" uppercase form.
+ */
+function api_normalize_role_code(string $code): string
{
- $hierarchy = Container::$container->getParameter('security.role_hierarchy.roles');
- $roles = [];
- array_walk_recursive($hierarchy, function ($role) use (&$roles) {
- $roles[$role] = $role;
- });
+ $code = strtoupper(trim($code));
+ return str_starts_with($code, 'ROLE_') ? $code : 'ROLE_'.$code;
+}
- return $roles;
+/**
+ * Priority when deriving legacy status from roles (first match wins).
+ */
+function api_roles_priority(): array
+{
+ return [
+ 'ROLE_SESSION_MANAGER',
+ 'ROLE_HR',
+ 'ROLE_TEACHER',
+ 'ROLE_STUDENT_BOSS',
+ 'ROLE_INVITEE',
+ 'ROLE_STUDENT',
+ ];
+}
+
+/**
+ * Canonical role -> legacy status map.
+ */
+function api_role_status_map(): array
+{
+ return [
+ 'ROLE_SESSION_MANAGER' => SESSIONADMIN,
+ 'ROLE_HR' => DRH,
+ 'ROLE_TEACHER' => COURSEMANAGER,
+ 'ROLE_STUDENT_BOSS' => STUDENT_BOSS,
+ 'ROLE_INVITEE' => INVITEE,
+ 'ROLE_STUDENT' => STUDENT,
+ ];
+}
+
+/**
+ * Returns legacy status from a set of roles using priority.
+ * Defaults to STUDENT if none matched.
+ */
+function api_status_from_roles(array $roles): int
+{
+ $norm = array_map('api_normalize_role_code', $roles);
+ $priority = api_roles_priority();
+ $map = api_role_status_map();
+
+ foreach ($priority as $p) {
+ if (in_array($p, $norm, true)) {
+ return $map[$p];
+ }
+ }
+ return STUDENT;
}
function api_get_user_roles(): array