Skip to content

Commit 1e0b509

Browse files
Merge pull request #5178 from christianbeeznest/social-fixes
Social: Implement online status tracking and friend list enhancements
2 parents 7180352 + ac3527b commit 1e0b509

File tree

10 files changed

+155
-37
lines changed

10 files changed

+155
-37
lines changed

assets/css/scss/_social.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,3 +976,11 @@
976976
}
977977
}
978978
}
979+
980+
.circle-green {
981+
color: green;
982+
}
983+
984+
.circle-gray {
985+
color: gray;
986+
}

assets/vue/components/social/MyFriendsCard.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
<a :href="`/social?id=${friend.friend.id}`" class="d-flex align-items-center text-decoration-none">
2929
<BaseUserAvatar :image-url="friend.friend.illustrationUrl" class="mr-2" />
3030
<span>{{ friend.friend.firstname }} {{ friend.friend.lastname }} <small class="text-muted">({{ friend.friend.username }})</small></span>
31+
<span v-if="friend.friend.isOnline" class="mdi mdi-circle circle-green mx-2" title="Online"></span>
32+
<span v-else class="mdi mdi-circle circle-gray mx-2" title="Offline"></span>
3133
</a>
3234
</li>
3335
</ul>
@@ -84,6 +86,14 @@ async function fetchFriends(userId) {
8486
},
8587
})
8688
friends.value = response.data['hydra:member']
89+
90+
const friendIds = friends.value.map(friend => friend.friend.id)
91+
const onlineStatusResponse = await axios.post(`/social-network/online-status`, { userIds: friendIds })
92+
const onlineStatuses = onlineStatusResponse.data
93+
94+
friends.value.forEach(friend => {
95+
friend.friend.isOnline = onlineStatuses[friend.friend.id] || false
96+
})
8797
} catch (error) {
8898
console.error('Error fetching friends:', error)
8999
}

assets/vue/views/social/SocialSearch.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
<div class="flex items-center">
5050
<img :src="user.avatar" class="w-16 h-16 rounded-full mr-4">
5151
<span>{{ user.name }}</span>
52-
<span v-if="user.status === 'online'" class="mdi mdi-circle green mx-2" title="Online"></span>
53-
<span v-else class="mdi mdi-circle gray mx-2" title="Offline"></span>
52+
<span v-if="user.status === 'online'" class="mdi mdi-circle circle-green mx-2" title="Online"></span>
53+
<span v-else class="mdi mdi-circle circle-gray mx-2" title="Offline"></span>
5454
<span :class="getRoleIcon(user.role)" class="mx-2"></span>
5555
</div>
5656
<div>

src/CoreBundle/Controller/SocialController.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,20 @@ public function getUserRelation(int $currentUserId, int $profileUserId, EntityMa
736736
]);
737737
}
738738

739+
#[Route('/online-status', name: 'chamilo_core_social_get_online_status', methods: ['POST'])]
740+
public function getOnlineStatus(Request $request, TrackEOnlineRepository $trackOnlineRepository): JsonResponse
741+
{
742+
$data = json_decode($request->getContent(), true);
743+
$userIds = $data['userIds'] ?? [];
744+
745+
$onlineStatuses = [];
746+
foreach ($userIds as $userId) {
747+
$onlineStatuses[$userId] = $trackOnlineRepository->isUserOnline($userId);
748+
}
749+
750+
return $this->json($onlineStatuses);
751+
}
752+
739753
/**
740754
* Checks the relationship between the current user and another user.
741755
*

src/CoreBundle/Entity/TrackELogin.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace Chamilo\CoreBundle\Entity;
88

9+
use Chamilo\CoreBundle\Repository\TrackELoginRepository;
910
use DateTime;
1011
use Doctrine\ORM\Mapping as ORM;
1112

@@ -15,7 +16,7 @@
1516
#[ORM\Table(name: 'track_e_login')]
1617
#[ORM\Index(name: 'login_user_id', columns: ['login_user_id'])]
1718
#[ORM\Index(name: 'idx_track_e_login_date', columns: ['login_date'])]
18-
#[ORM\Entity]
19+
#[ORM\Entity(repositoryClass: TrackELoginRepository::class)]
1920
class TrackELogin
2021
{
2122
#[ORM\Column(name: 'login_id', type: 'integer')]

src/CoreBundle/EventListener/LoginSuccessHandler.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66

77
namespace Chamilo\CoreBundle\EventListener;
88

9+
use Chamilo\CoreBundle\Entity\TrackELogin;
10+
use Chamilo\CoreBundle\Entity\TrackEOnline;
911
use Chamilo\CoreBundle\Entity\User;
12+
use Chamilo\CoreBundle\Repository\TrackELoginRepository;
13+
use Chamilo\CoreBundle\Repository\TrackEOnlineRepository;
1014
use Chamilo\CoreBundle\Settings\SettingsManager;
15+
use Doctrine\ORM\EntityManagerInterface;
1116
use Symfony\Component\HttpFoundation\RedirectResponse;
1217
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
1318
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
@@ -20,15 +25,18 @@ class LoginSuccessHandler
2025
protected UrlGeneratorInterface $router;
2126
protected AuthorizationCheckerInterface $checker;
2227
protected SettingsManager $settingsManager;
28+
protected EntityManagerInterface $entityManager;
2329

2430
public function __construct(
2531
UrlGeneratorInterface $urlGenerator,
2632
AuthorizationCheckerInterface $checker,
27-
SettingsManager $settingsManager
33+
SettingsManager $settingsManager,
34+
EntityManagerInterface $entityManager
2835
) {
2936
$this->router = $urlGenerator;
3037
$this->checker = $checker;
3138
$this->settingsManager = $settingsManager;
39+
$this->entityManager = $entityManager;
3240
}
3341

3442
/**
@@ -37,6 +45,7 @@ public function __construct(
3745
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
3846
{
3947
$request = $event->getRequest();
48+
$session = $request->getSession();
4049

4150
/** @var User $user */
4251
$user = $event->getAuthenticationToken()->getUser();
@@ -128,6 +137,21 @@ public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
128137
}
129138
}
130139

140+
if (!$session->get('login_records_created')) {
141+
$userIp = $request->getClientIp();
142+
143+
/** @var TrackEOnlineRepository $trackEOnlineRepository */
144+
$trackEOnlineRepository = $this->entityManager->getRepository(TrackEOnline::class);
145+
146+
/** @var TrackELoginRepository $trackELoginRepository */
147+
$trackELoginRepository = $this->entityManager->getRepository(TrackELogin::class);
148+
149+
$trackELoginRepository->createLoginRecord($user, new \DateTime(), $userIp);
150+
$trackEOnlineRepository->createOnlineSession($user, $userIp);
151+
152+
$session->set('login_records_created', true);
153+
}
154+
131155
if (!empty($url)) {
132156
$response = new RedirectResponse($url);
133157
}

src/CoreBundle/EventListener/LogoutListener.php

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
namespace Chamilo\CoreBundle\EventListener;
88

9+
use Chamilo\CoreBundle\Entity\TrackELogin;
10+
use Chamilo\CoreBundle\Entity\TrackEOnline;
911
use Chamilo\CoreBundle\Entity\User;
10-
use Database;
1112
use Doctrine\ORM\EntityManagerInterface;
12-
use Symfony\Component\HttpFoundation\JsonResponse;
1313
use Symfony\Component\HttpFoundation\RedirectResponse;
1414
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
1515
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
@@ -42,15 +42,11 @@ public function onSymfonyComponentSecurityHttpEventLogoutEvent(LogoutEvent $even
4242
{
4343
$request = $event->getRequest();
4444

45-
// Chamilo logout
45+
// Chamilo logout operations
4646
$request->getSession()->remove('_selected_locale');
4747
$request->getSession()->remove('_locale');
4848
$request->getSession()->remove('_locale_user');
4949

50-
/*if (api_is_global_chat_enabled()) {
51-
$chat = new \Chat();
52-
$chat->setUserStatus(0);
53-
}*/
5450
$token = $this->storage->getToken();
5551
if (null === $token) {
5652
$login = $this->router->generate('index');
@@ -62,40 +58,20 @@ public function onSymfonyComponentSecurityHttpEventLogoutEvent(LogoutEvent $even
6258
$user = $token->getUser();
6359
if ($user instanceof User) {
6460
$userId = $user->getId();
65-
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
66-
67-
$sql = "SELECT login_id, login_date
68-
FROM {$table}
69-
WHERE login_user_id = {$userId}
70-
ORDER BY login_date DESC
71-
LIMIT 0,1";
72-
$loginId = null;
73-
$connection = $this->em->getConnection();
74-
$result = $connection->executeQuery($sql);
75-
if ($result->rowCount() > 0) {
76-
$row = $result->fetchAssociative();
77-
if ($row) {
78-
$loginId = $row['login_id'];
79-
}
80-
}
8161

62+
$trackELoginRepository = $this->em->getRepository(TrackELogin::class);
8263
$loginAs = $this->checker->isGranted('ROLE_PREVIOUS_ADMIN');
8364
if (!$loginAs) {
84-
$current_date = api_get_utc_datetime();
85-
$sql = "UPDATE {$table}
86-
SET logout_date='".$current_date."'
87-
WHERE login_id='{$loginId}'";
88-
$connection->executeQuery($sql);
65+
$currentDate = new \DateTime("now", new \DateTimeZone('UTC'));
66+
$trackELoginRepository->updateLastLoginLogoutDate($userId, $currentDate);
8967
}
9068

91-
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
92-
$sql = "DELETE FROM $table WHERE login_user_id = $userId";
93-
$connection->executeQuery($sql);
69+
$trackEOnlineRepository = $this->em->getRepository(TrackEOnline::class);
70+
$trackEOnlineRepository->removeOnlineSessionsByUser($userId);
9471
}
9572

9673
$login = $this->router->generate('index');
9774

9875
return new RedirectResponse($login);
99-
// return new JsonResponse('logout out', 200);
10076
}
10177
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/* For licensing terms, see /license.txt */
6+
7+
namespace Chamilo\CoreBundle\Repository;
8+
9+
use Chamilo\CoreBundle\Entity\TrackELogin;
10+
use Chamilo\CoreBundle\Entity\User;
11+
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
12+
use Doctrine\Persistence\ManagerRegistry;
13+
use DateTime;
14+
15+
class TrackELoginRepository extends ServiceEntityRepository
16+
{
17+
public function __construct(ManagerRegistry $registry)
18+
{
19+
parent::__construct($registry, TrackELogin::class);
20+
}
21+
22+
public function createLoginRecord(User $user, DateTime $loginDate, string $userIp): TrackELogin
23+
{
24+
$loginRecord = new TrackELogin();
25+
$loginRecord->setUser($user);
26+
$loginRecord->setLoginDate($loginDate);
27+
$loginRecord->setUserIp($userIp);
28+
29+
$this->_em->persist($loginRecord);
30+
$this->_em->flush();
31+
32+
return $loginRecord;
33+
}
34+
35+
public function updateLastLoginLogoutDate(int $userId, DateTime $logoutDate): void
36+
{
37+
$lastLoginId = $this->createQueryBuilder('t')
38+
->select('t.loginId')
39+
->where('t.user = :userId')
40+
->andWhere('t.logoutDate IS NULL')
41+
->setParameter('userId', $userId)
42+
->orderBy('t.loginDate', 'DESC')
43+
->setMaxResults(1)
44+
->getQuery()
45+
->getSingleScalarResult();
46+
47+
if ($lastLoginId) {
48+
$qb = $this->createQueryBuilder('t')
49+
->update()
50+
->set('t.logoutDate', ':logoutDate')
51+
->where('t.loginId = :loginId')
52+
->setParameter('loginId', $lastLoginId)
53+
->setParameter('logoutDate', $logoutDate);
54+
55+
$qb->getQuery()->execute();
56+
}
57+
}
58+
}

src/CoreBundle/Repository/TrackEOnlineRepository.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
namespace Chamilo\CoreBundle\Repository;
88

99
use Chamilo\CoreBundle\Entity\TrackEOnline;
10+
use Chamilo\CoreBundle\Entity\User;
1011
use Chamilo\CoreBundle\Settings\SettingsManager;
1112
use DateTime;
1213
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
@@ -50,4 +51,29 @@ public function isUserOnline(int $userId): bool
5051
return false;
5152
}
5253
}
54+
55+
public function createOnlineSession(User $user, string $userIp, int $cId = 0, int $sessionId = 0, int $accessUrlId = 1): void
56+
{
57+
$trackEOnline = new TrackEOnline();
58+
$trackEOnline->setLoginUserId($user->getId());
59+
$trackEOnline->setLoginDate(new \DateTime());
60+
$trackEOnline->setUserIp($userIp);
61+
$trackEOnline->setCId($cId);
62+
$trackEOnline->setSessionId($sessionId);
63+
$trackEOnline->setAccessUrlId($accessUrlId);
64+
65+
$this->_em->persist($trackEOnline);
66+
$this->_em->flush();
67+
}
68+
69+
public function removeOnlineSessionsByUser(int $userId): void
70+
{
71+
$sessions = $this->findBy(['loginUserId' => $userId]);
72+
73+
foreach ($sessions as $session) {
74+
$this->_em->remove($session);
75+
}
76+
77+
$this->_em->flush();
78+
}
5379
}

src/CoreBundle/Resources/config/listeners.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,12 @@ services:
6464

6565
# Auth listeners
6666
Chamilo\CoreBundle\EventListener\LoginSuccessHandler:
67-
arguments: ['@router', '@security.authorization_checker', '@Chamilo\CoreBundle\Settings\SettingsManager']
67+
arguments: ['@router', '@security.authorization_checker', '@Chamilo\CoreBundle\Settings\SettingsManager', '@doctrine.orm.entity_manager']
6868
tags:
6969
- {name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin}
7070

7171
Chamilo\CoreBundle\EventListener\LogoutListener:
72+
arguments: ['@router', '@security.authorization_checker', '@security.token_storage', '@doctrine.orm.entity_manager']
7273
tags:
7374
- name: kernel.event_listener
7475
event: Symfony\Component\Security\Http\Event\LogoutEvent

0 commit comments

Comments
 (0)