diff --git a/public/main/admin/usergroups.php b/public/main/admin/usergroups.php index 3904e12d615..7072346a5cc 100644 --- a/public/main/admin/usergroups.php +++ b/public/main/admin/usergroups.php @@ -65,6 +65,7 @@ .' '.Display::getMdiIcon(ObjectIcon::USER, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Subscribe users to class')).'' .' '.Display::getMdiIcon(ObjectIcon::COURSE, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Subscribe class to courses')).'' .' '.Display::getMdiIcon(ObjectIcon::SESSION, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Subscribe class to sessions')).'' + .' '.Display::getMdiIcon(ObjectIcon::USER_LIST, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Overview students and courses subscribed to the class')).'' .' '.Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Edit')).'' .' '.Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')).'\'; }'; diff --git a/public/main/inc/ajax/model.ajax.php b/public/main/inc/ajax/model.ajax.php index 298136e4ade..22bba050724 100644 --- a/public/main/inc/ajax/model.ajax.php +++ b/public/main/inc/ajax/model.ajax.php @@ -2585,13 +2585,24 @@ function getWhereClause($col, $oper, $val) $course_id, api_get_session_id() )) { - $url = 'class.php?action=remove_class_from_course&id='.$group['id'].'&'.api_get_cidreq( - ).'&id_session='.api_get_session_id(); - $icon = Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Remove')); + $actions = [ + [ + 'icon' => Display::getMdiIcon(ObjectIcon::USER_LIST, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Overview students subscribed to the class')), + 'url' => 'overview?usergroup='.$group['id'].'&course='.$course_id, + ], + [ + 'icon' => Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Remove')), + 'url' => 'class.php?action=remove_class_from_course&id=' . $group['id'] . '&' . api_get_cidreq() . '&id_session=' . api_get_session_id(), + 'onclick' => "if (!confirm('". get_lang('Are you sure you want to remove the class') . "')) return false;" + ], + ]; } else { - $url = 'class.php?action=add_class_to_course&id='.$group['id'].'&'.api_get_cidreq( - ).'&type=not_registered'; - $icon = Display::getMdiIcon(ActionIcon::ADD, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Add')); + $actions = [ + [ + 'icon' => Display::getMdiIcon(ActionIcon::ADD, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Add')), + 'url' => 'class.php?action=add_class_to_course&id=' . $group['id'] . '&' . api_get_cidreq() . '&type=not_registered', + ] + ]; } switch ($group['group_type']) { @@ -2611,10 +2622,17 @@ function getWhereClause($col, $oper, $val) if ($obj->allowTeachers() && $group['author_id'] == $currentUserId) { $group['actions'] .= Display::url( Display::getMdiIcon(ToolIcon::TRACKING, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Statistics')), - $urlUserGroup.'&id='.$group['id'] - ).' '; + $urlUserGroup . '&id=' . $group['id'] + ) . ' '; + } + + for ($i = 0; $i < count($actions); $i++) { + $group['actions'] .= Display::url( + $actions[$i]['icon'], + $actions[$i]['url'] ?? null, + ['onclick' => $actions[$i]['onclick'] ?? ''] + ); } - $group['actions'] .= Display::url($icon, $url); } $new_result[] = $group; } diff --git a/public/main/inc/lib/usergroup.lib.php b/public/main/inc/lib/usergroup.lib.php index 36d3177a5f6..8b9afa79515 100644 --- a/public/main/inc/lib/usergroup.lib.php +++ b/public/main/inc/lib/usergroup.lib.php @@ -8,7 +8,10 @@ use Chamilo\CoreBundle\Enums\ObjectIcon; use Chamilo\CoreBundle\Enums\ToolIcon; use Chamilo\CoreBundle\Framework\Container; +use Chamilo\CoreBundle\Service\StandardizationService; use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Polyfill\Intl\Normalizer\Normalizer; + /** * Class UserGroup. @@ -1209,6 +1212,36 @@ public function unsubscribe_courses_from_usergroup($usergroup_id, $delete_items, } } + public function unsubscribe_only_courses_from_usergroup($usergroup_id, $delete_items, $sessionId = 0) + { + $sessionId = (int) $sessionId; + // Deleting items. + if (!empty($delete_items)) { + $user_list = $this->get_users_by_usergroup($usergroup_id); + + $groupId = isset($_GET['id']) ? (int) $_GET['id'] : 0; + foreach ($delete_items as $course_id) { + $course_info = api_get_course_info_by_id($course_id); + if ($course_info) { + Database::delete( + $this->usergroup_rel_course_table, + [ + 'usergroup_id = ? AND course_id = ?' => [ + $usergroup_id, + $course_id, + ], + ] + ); + } + if (0 != $sessionId && 0 != $groupId) { + $this->subscribe_sessions_to_usergroup($groupId, [0]); + } else { + $s = $sessionId; + } + } + } + } + /** * Subscribe users to a group. * @@ -1799,7 +1832,7 @@ public function getUserListByUserGroup($id, $orderBy = '') INNER JOIN $this->usergroup_rel_user_table c ON c.user_id = u.id WHERE u.active <> ".USER_SOFT_DELETED." AND c.usergroup_id = $id" - ; + ; if (!empty($orderBy)) { $orderBy = Database::escape_string($orderBy); $sql .= " ORDER BY $orderBy "; @@ -1838,7 +1871,7 @@ public function setForm($form, $type = 'add', Usergroup $userGroup = null) true, false, [ - 'ToolbarSet' => 'Minimal', + 'ToolbarSet' => 'Minimal', ] ); $form->applyFilter('description', 'trim'); @@ -2587,7 +2620,7 @@ public function show_group_column_information($group_id, $user_id, $show = '') // I'm just a reader $relation_group_title = get_lang('I am a reader'); $links .= '
  • '. - Display::getMdiIcon(ObjectIcon::INVITATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Invite friends')).get_lang('Invite friends').'
  • '; + Display::getMdiIcon(ObjectIcon::INVITATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Invite friends')).get_lang('Invite friends').''; if (self::canLeave($group_info)) { $links .= '
  • '. Display::getMdiIcon(ActionIcon::EXIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Leave group')).get_lang('Leave group').'
  • '; @@ -2596,11 +2629,11 @@ public function show_group_column_information($group_id, $user_id, $show = '') case GROUP_USER_PERMISSION_ADMIN: $relation_group_title = get_lang('I am an admin'); $links .= '
  • '. - Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Edit this group')).get_lang('Edit this group').'
  • '; + Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Edit this group')).get_lang('Edit this group').''; $links .= '
  • '. - Display::getMdiIcon(ObjectIcon::WAITING_LIST, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Waiting list')).get_lang('Waiting list').'
  • '; + Display::getMdiIcon(ObjectIcon::WAITING_LIST, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Waiting list')).get_lang('Waiting list').''; $links .= '
  • '. - Display::getMdiIcon(ObjectIcon::INVITATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Invite friends')).get_lang('Invite friends').'
  • '; + Display::getMdiIcon(ObjectIcon::INVITATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Invite friends')).get_lang('Invite friends').''; if (self::canLeave($group_info)) { $links .= '
  • '. Display::getMdiIcon(ActionIcon::EXIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Leave group')).get_lang('Leave group').'
  • '; @@ -2619,10 +2652,10 @@ public function show_group_column_information($group_id, $user_id, $show = '') //$links .= '
  • '. Display::getMdiIcon(ObjectIcon::GROUP, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Members list')).''.get_lang('Members list').'
  • '; if (GROUP_PERMISSION_CLOSED == $group_info['visibility']) { $links .= '
  • '. - Display::getMdiIcon(ObjectIcon::WAITING_LIST, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Waiting list')).get_lang('Waiting list').'
  • '; + Display::getMdiIcon(ObjectIcon::WAITING_LIST, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Waiting list')).get_lang('Waiting list').''; } $links .= '
  • '. - Display::getMdiIcon(ObjectIcon::INVITATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Invite friends')).get_lang('Invite friends').'
  • '; + Display::getMdiIcon(ObjectIcon::INVITATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Invite friends')).get_lang('Invite friends').''; if (self::canLeave($group_info)) { $links .= '
  • '. Display::getMdiIcon(ActionIcon::EXIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Leave group')).get_lang('Leave group').'
  • '; @@ -2631,15 +2664,15 @@ public function show_group_column_information($group_id, $user_id, $show = '') case GROUP_USER_PERMISSION_HRM: $relation_group_title = get_lang('I am a human resources manager'); $links .= '
  • '. - Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Create thread')).get_lang('Create thread').'
  • '; + Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Create thread')).get_lang('Create thread').''; $links .= '
  • '. - Display::getMdiIcon(ToolIcon::MESSAGE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Messages list')).get_lang('Messages list').'
  • '; + Display::getMdiIcon(ToolIcon::MESSAGE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Messages list')).get_lang('Messages list').''; $links .= '
  • '. - Display::getMdiIcon(ObjectIcon::INVITATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Invite friends')).get_lang('Invite friends').'
  • '; + Display::getMdiIcon(ObjectIcon::INVITATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Invite friends')).get_lang('Invite friends').''; $links .= '
  • '. - Display::getMdiIcon(ObjectIcon::GROUP, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Members list')).get_lang('Members list').'
  • '; + Display::getMdiIcon(ObjectIcon::GROUP, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Members list')).get_lang('Members list').''; $links .= '
  • '. - Display::getMdiIcon(ActionIcon::EXIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Leave group')).get_lang('Leave group').'
  • '; + Display::getMdiIcon(ActionIcon::EXIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Leave group')).get_lang('Leave group').''; break; default: //$links .= '
  • '.Display::return_icon('addd.gif', get_lang('Join group'), array('hspace'=>'6')).'
  • '; @@ -3085,6 +3118,92 @@ public function getGroupsByLpCategory($categoryId, $courseId, $sessionId) return Database::store_result($result, 'ASSOC'); } + public function getUsersInAndOutOfCourse($usergroupId, $courseId): array + { + $usergroupModel = new UserGroupModel(); + + $usersInUsergroup = $usergroupModel->get_users_by_usergroup($usergroupId); + + $data = []; + $data['error'] = null; + $data['warning'] = null; + $data['usersSubscribedToCourse'] = []; + $data['usersNotSubscribedToCourse'] = []; + + if (sizeof($usersInUsergroup) > 0) { + + $userManager = new UserManager(); + $userRepository = $userManager->getRepository(); + + $courseManager = new CourseManager(); + $usersInCourse = $courseManager->get_user_list_from_course_code($courseId); + + $usersSubscribedToCourse = []; + $usersNotSubscribedToCourse = []; + foreach ($usersInUsergroup as $userId) { + $user = $userRepository->find($userId); + if (array_key_exists($userId, $usersInCourse)) { + $usersSubscribedToCourse[] = $user; + } else { + $usersNotSubscribedToCourse[] = $user; + } + } + + $data['usersSubscribedToCourse'] = StandardizationService::sortByNameByCountryAndStandardizeName($usersSubscribedToCourse, true); + $data['usersNotSubscribedToCourse'] = StandardizationService::sortByNameByCountryAndStandardizeName($usersNotSubscribedToCourse, true);; + } else { + $data['warning'] = get_lang('No user is subscribed to this class'); + } + + return $data; + } + + public function getUsersAndCoursesSubscribedToAUserGroup($usergroupId):array + { + $data = []; + $data['error'] = null; + $data['warning'] = null; + $data['usersSubscribedToUsergroup'] = []; + $data['coursesSubscribedToUsergroup'] = []; + + $usergroupLib = new UserGroupModel(); + + $userManager = new UserManager(); + $userRepository = $userManager->getRepository(); + $usersSubscribedToUsergroupIds = $usergroupLib->get_users_by_usergroup($usergroupId); + if (count($usersSubscribedToUsergroupIds) > 0) { + $usersSubscribedToUsergroup = []; + foreach ($usersSubscribedToUsergroupIds as $userId) { + $usersSubscribedToUsergroup[] = $userRepository->find($userId); + } + $data['usersSubscribedToUsergroup'] = StandardizationService::sortByNameByCountryAndStandardizeName($usersSubscribedToUsergroup, true); + } + + $courseManager = new CourseManager(); + $coursesSubscribedToUsergroupIds = $usergroupLib->get_courses_by_usergroup($usergroupId); + if (count($coursesSubscribedToUsergroupIds) > 0) { + $coursesSubscribedToUsergroup = []; + foreach ($coursesSubscribedToUsergroupIds as $courseId) { + $coursesSubscribedToUsergroup[] = [ + 'code' => $courseManager->get_course_code_from_course_id($courseId), + 'name' => $courseManager->getCourseNameFromCode($courseManager->get_course_code_from_course_id($courseId)), + ]; + } + $data['coursesSubscribedToUsergroup'] = StandardizationService::sort($coursesSubscribedToUsergroup); + } + + if (count($usersSubscribedToUsergroupIds) + count($coursesSubscribedToUsergroupIds) == 0) { + $data['warning'] = get_lang('No user and no course are subscribed to this class'); + } else if (count($usersSubscribedToUsergroupIds) == 0) { + $data['warning'] = get_lang('No user are subscribed to this class'); + } else if (count($coursesSubscribedToUsergroupIds) == 0) { + $data['warning'] = get_lang('No course are subscribed to this class'); + } + + return $data; + } + + public static function getRoleName($relation) { switch ((int) $relation) { diff --git a/src/CoreBundle/Controller/ClassController.php b/src/CoreBundle/Controller/ClassController.php new file mode 100644 index 00000000000..bb89cb52b62 --- /dev/null +++ b/src/CoreBundle/Controller/ClassController.php @@ -0,0 +1,30 @@ +get($id); + + $data = $usergroupLib->getUsersAndCoursesSubscribedToAUserGroup($id); + + return $this->render('@ChamiloCore/Class/overview.html.twig', [ + 'usergroupName' => $usergroup['title'], + 'usersSubscribedToUsergroup' => $data['usersSubscribedToUsergroup'], + 'coursesSubscribedToUsergroup' => $data['coursesSubscribedToUsergroup'], + 'warning' => $data['warning'], + 'error' => $data['error'], + ]); + } +} diff --git a/src/CoreBundle/Controller/UserController.php b/src/CoreBundle/Controller/UserController.php index d30972b0e2a..a1dd84101bd 100644 --- a/src/CoreBundle/Controller/UserController.php +++ b/src/CoreBundle/Controller/UserController.php @@ -8,21 +8,48 @@ use Chamilo\CoreBundle\Repository\Node\IllustrationRepository; use Chamilo\CoreBundle\Repository\Node\UserRepository; +use CourseManager; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Core\User\UserInterface; +use UserGroupModel; /** * @author Julio Montoya */ -#[Route('/user')] class UserController extends AbstractController { + #[Route(path: '/main/user/overview', name: 'overview_class', methods: ['GET'])] + public function overview(Request $request): Response + { + $usergroupId = $request->query->get('usergroup'); + $courseId = $request->query->get('course'); + + $usergroupLib = new UserGroupModel(); + $usergroup = $usergroupLib->get($usergroupId); + + $courseLib = new CourseManager(); + $courseName = $courseLib->getCourseNameFromCode($courseLib->get_course_code_from_course_id($courseId)); + + $data = $usergroupLib->getUsersInAndOutOfCourse($usergroupId, $courseId); + + return $this->render('@ChamiloCore/User/overview.html.twig', [ + 'courseId' => $courseId, + 'courseName' => $courseName, + 'usergroupName' => $usergroup['title'], + 'usersSubscribedToCourse' => $data['usersSubscribedToCourse'], + 'usersNotSubscribedToCourse' => $data['usersNotSubscribedToCourse'], + 'error' => $data['error'], + 'warning' => $data['warning'], + ]); + } + /** * Public profile. */ - #[Route(path: '/{username}', methods: ['GET'], name: 'chamilo_core_user_profile')] + #[Route(path: '/user/{username}', name: 'chamilo_core_user_profile', methods: ['GET'])] public function profile(string $username, UserRepository $userRepository, IllustrationRepository $illustrationRepository): Response { $user = $userRepository->findByUsername($username); @@ -38,4 +65,5 @@ public function profile(string $username, UserRepository $userRepository, Illust 'illustration_url' => $url, ]); } + } diff --git a/src/CoreBundle/Resources/views/Class/overview.html.twig b/src/CoreBundle/Resources/views/Class/overview.html.twig new file mode 100644 index 00000000000..51935d25e36 --- /dev/null +++ b/src/CoreBundle/Resources/views/Class/overview.html.twig @@ -0,0 +1,98 @@ +{% extends "@ChamiloCore/Layout/base-layout.html.twig" %} + +{% block chamilo_wrap %} + + {%- include '@ChamiloCore/Layout/vue_setup.html.twig' %} + +
    + + {%- block content %} + +
    +
    + +
    + + + +

    {{ 'Overview class'|trans }} : {{ usergroupName }}

    +
    + +
    + + {% if error is not null %} +
    +

    {{ error }}

    +
    + {% endif %} + + {% if warning is not null %} +
    +

    {{ warning }}

    +
    + {% endif %} + + {% if (usersSubscribedToUsergroup|length + coursesSubscribedToUsergroup|length) > 0 %} +
    + + +
    + {% endif %} + + {% if usersSubscribedToUsergroup|length > 0 %} +
    + {{ 'Users subscribed to this class'|trans }} +
      + {% for user in usersSubscribedToUsergroup %} +
    • {{ user.identity }} ( {{ user.email }} )
    • + {% endfor %} +
    +
    + {% endif %} + + {% if coursesSubscribedToUsergroup|length > 0 %} +
    + {{ 'Courses subscribed to the class'|trans }} +
      + {% for course in coursesSubscribedToUsergroup %} +
    • [{{ course.code }}] {{ course.name }}
    • + {% endfor %} +
    +
    + {% endif %} + + {% endblock %} + +
    + + + +{% endblock %} + + diff --git a/src/CoreBundle/Resources/views/User/overview.html.twig b/src/CoreBundle/Resources/views/User/overview.html.twig new file mode 100644 index 00000000000..4be58029f6a --- /dev/null +++ b/src/CoreBundle/Resources/views/User/overview.html.twig @@ -0,0 +1,106 @@ +{% extends "@ChamiloCore/Layout/base-layout.html.twig" %} + +{% block chamilo_wrap %} + + {%- include '@ChamiloCore/Layout/vue_setup.html.twig' %} + +
    + {%- block content %} + +
    +
    + +
    + + + +

    {{ 'Overview class'|trans }} : {{ usergroupName }}

    +
    + +
    + + {% if error is not null %} +
    +

    {{ error }}

    +
    + {% endif %} + + {% if warning is not null %} +
    +

    {{ warning }}

    +
    + {% endif %} + + {% if (usersSubscribedToCourse|length + usersNotSubscribedToCourse|length) > 0 %} +
    + + +
    + {% endif %} + + {% if usersSubscribedToCourse|length > 0 %} +
    + {{ 'Users subscribed to this course'|trans }} +
      + {% for user in usersSubscribedToCourse %} +
    • {{ user.identity }} ( {{ user.email }} )
    • + {% endfor %} +
    +
    + {% endif %} + + {% if usersNotSubscribedToCourse|length > 0 %} +
    + {{ 'Users not subscribed to this course'|trans }} +
      + {% for user in usersNotSubscribedToCourse %} +
    • {{ user.identity }} ( {{ user.email }} )
    • + {% endfor %} +
    +
    + {% endif %} + + {% endblock %} +
    + + + +{% endblock %} diff --git a/src/CoreBundle/Service/StandardizationService.php b/src/CoreBundle/Service/StandardizationService.php new file mode 100644 index 00000000000..73e2ec6eeb1 --- /dev/null +++ b/src/CoreBundle/Service/StandardizationService.php @@ -0,0 +1,72 @@ +getLastName()), self::standardizeString($b->getLastName())); + }); + } else { + usort($users, function ($a, $b) { + return strcmp(self::standardizeString($a->getFirstName()), self::standardizeString($b->getFirstName())); + }); + } + + $standardizedNames = []; + if ($addEmail) { + foreach ($users as $user) { + $standardizedNames[] = [ + 'identity' => self::standardizeName($user, $countryCode), + 'email' => $user->getEmail() + ]; + } + } else { + foreach ($users as $user) { + $standardizedNames[] = ['identity' => self::standardizeName($user, $countryCode)]; + } + } + return $standardizedNames; + } + + public static function standardizeName(User $user, string $countryCode = null): string + { + if ($countryCode === null) { + $countryCode = api_get_language_isocode(); + } + + return ucwords(api_get_person_name($user->getFirstName(), $user->getLastName(), null, null, $countryCode)); + } + + public static function standardizeString(string $string): string + { + $string = trim($string, " \t\n\r\0\x0B()"); + $string = preg_replace('/[\x{0300}-\x{036f}]/u', '', normalizer_normalize($string, Normalizer::FORM_D)); + return mb_strtolower($string, 'UTF-8'); + } + +}