Passed
Pull Request — master (#5329)
by Angel Fernando Quiroz
07:09
created

SocialController::getInvitationsCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 3
dl 0
loc 16
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Controller;
8
9
use Chamilo\CoreBundle\Entity\ExtraField;
10
use Chamilo\CoreBundle\Entity\Legal;
11
use Chamilo\CoreBundle\Entity\Message;
12
use Chamilo\CoreBundle\Entity\MessageAttachment;
13
use Chamilo\CoreBundle\Entity\User;
14
use Chamilo\CoreBundle\Entity\Usergroup;
15
use Chamilo\CoreBundle\Entity\UserRelUser;
16
use Chamilo\CoreBundle\Repository\ExtraFieldOptionsRepository;
17
use Chamilo\CoreBundle\Repository\ExtraFieldRepository;
18
use Chamilo\CoreBundle\Repository\LanguageRepository;
19
use Chamilo\CoreBundle\Repository\LegalRepository;
20
use Chamilo\CoreBundle\Repository\MessageRepository;
21
use Chamilo\CoreBundle\Repository\Node\IllustrationRepository;
22
use Chamilo\CoreBundle\Repository\Node\MessageAttachmentRepository;
23
use Chamilo\CoreBundle\Repository\Node\UsergroupRepository;
24
use Chamilo\CoreBundle\Repository\Node\UserRepository;
25
use Chamilo\CoreBundle\Repository\TrackEOnlineRepository;
26
use Chamilo\CoreBundle\Serializer\UserToJsonNormalizer;
27
use Chamilo\CoreBundle\Settings\SettingsManager;
28
use Chamilo\CourseBundle\Repository\CForumThreadRepository;
29
use DateTime;
30
use DateTimeInterface;
31
use Doctrine\ORM\EntityManagerInterface;
32
use Exception;
33
use ExtraFieldValue;
34
use MessageManager;
35
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
36
use Symfony\Component\HttpFoundation\File\UploadedFile;
37
use Symfony\Component\HttpFoundation\JsonResponse;
38
use Symfony\Component\HttpFoundation\Request;
39
use Symfony\Component\HttpFoundation\RequestStack;
40
use Symfony\Component\HttpFoundation\Response;
41
use Symfony\Component\Mailer\MailerInterface;
42
use Symfony\Component\Mime\Email;
43
use Symfony\Component\Routing\Annotation\Route;
44
use Symfony\Contracts\Translation\TranslatorInterface;
45
use UserManager;
46
47
#[Route('/social-network')]
48
class SocialController extends AbstractController
49
{
50
    #[Route('/personal-data/{userId}', name: 'chamilo_core_social_personal_data')]
51
    public function getPersonalData(
52
        int $userId,
53
        SettingsManager $settingsManager,
54
        UserToJsonNormalizer $userToJsonNormalizer
55
    ): JsonResponse {
56
        $propertiesToJson = $userToJsonNormalizer->serializeUserData($userId);
57
        $properties = $propertiesToJson ? json_decode($propertiesToJson, true) : [];
58
59
        $officerData = [
60
            ['name' => $settingsManager->getSetting('profile.data_protection_officer_name')],
61
            ['role' => $settingsManager->getSetting('profile.data_protection_officer_role')],
62
            ['email' => $settingsManager->getSetting('profile.data_protection_officer_email')],
63
        ];
64
        $properties['officer_data'] = $officerData;
65
66
        $dataForVue = [
67
            'personalData' => $properties,
68
        ];
69
70
        return $this->json($dataForVue);
71
    }
72
73
    #[Route('/terms-and-conditions/{userId}', name: 'chamilo_core_social_terms')]
74
    public function getLegalTerms(
75
        int $userId,
76
        SettingsManager $settingsManager,
77
        TranslatorInterface $translator,
78
        LegalRepository $legalTermsRepo,
79
        UserRepository $userRepo,
80
        LanguageRepository $languageRepo
81
    ): JsonResponse {
82
        $user = $userRepo->find($userId);
83
        if (!$user) {
84
            return $this->json(['error' => 'User not found']);
85
        }
86
87
        $isoCode = $user->getLocale();
88
        $extraFieldValue = new ExtraFieldValue('user');
89
        $value = $extraFieldValue->get_values_by_handler_and_field_variable($userId, 'legal_accept');
90
        if ($value && !empty($value['value'])) {
91
            [$legalId, $legalLanguageId, $legalTime] = explode(':', $value['value']);
92
            $term = $legalTermsRepo->find($legalId);
93
        } else {
94
            $term = $this->getLastConditionByLanguage($languageRepo, $isoCode, $legalTermsRepo, $settingsManager);
95
        }
96
97
        if (!$term) {
98
            return $this->json(['error' => 'Terms not found']);
99
        }
100
101
        $termExtraFields = new ExtraFieldValue('terms_and_condition');
102
        $values = $termExtraFields->getAllValuesByItem($term->getId());
103
104
        $termsContent = [];
105
        foreach ($values as $value) {
106
            if (!empty($value['value'])) {
107
                $termsContent[] = [
108
                    'title' => $translator->trans($value['display_text'], [], 'messages', $isoCode),
109
                    'content' => $value['value'],
110
                ];
111
            }
112
        }
113
114
        if (empty($termsContent)) {
115
            $termsContent[] = [
116
                'title' => $translator->trans('Terms and Conditions', [], 'messages', $isoCode),
117
                'content' => $term->getContent(),
118
            ];
119
        }
120
121
        $formattedDate = new DateTime('@'.$term->getDate());
122
123
        $dataForVue = [
124
            'terms' => $termsContent,
125
            'date_text' => $translator->trans('PublicationDate', [], 'messages', $isoCode).': '.$formattedDate->format('Y-m-d H:i:s'),
126
        ];
127
128
        return $this->json($dataForVue);
129
    }
130
131
    #[Route('/legal-status/{userId}', name: 'chamilo_core_social_legal_status')]
132
    public function getLegalStatus(
133
        int $userId,
134
        SettingsManager $settingsManager,
135
        TranslatorInterface $translator,
136
        UserRepository $userRepo,
137
    ): JsonResponse {
138
        $allowTermsConditions = 'true' === $settingsManager->getSetting('registration.allow_terms_conditions');
139
140
        if (!$allowTermsConditions) {
141
            return $this->json([
142
                'message' => $translator->trans('No terms and conditions available', [], 'messages'),
143
            ]);
144
        }
145
146
        $user = $userRepo->find($userId);
147
        if (!$user) {
148
            return $this->json(['error' => 'User not found']);
149
        }
150
151
        $extraFieldValue = new ExtraFieldValue('user');
152
        $value = $extraFieldValue->get_values_by_handler_and_field_variable($userId, 'legal_accept');
153
154
        if (empty($value['value'])) {
155
            return $this->json([
156
                'isAccepted' => false,
157
                'message' => $translator->trans('Send legal agreement', [], 'messages'),
158
            ]);
159
        }
160
161
        [$legalId, $legalLanguageId, $legalTime] = explode(':', $value['value']);
162
        $dateTime = new DateTime("@$legalTime");
163
        $response = [
164
            'isAccepted' => true,
165
            'acceptDate' => $dateTime->format('Y-m-d H:i:s'),
166
            'message' => '',
167
        ];
168
169
        return $this->json($response);
170
    }
171
172
    #[Route('/send-legal-term', name: 'chamilo_core_social_send_legal_term')]
173
    public function sendLegalTerm(
174
        Request $request,
175
        SettingsManager $settingsManager,
176
        TranslatorInterface $translator,
177
        LegalRepository $legalTermsRepo,
178
        UserRepository $userRepo,
179
        LanguageRepository $languageRepo
180
    ): JsonResponse {
181
        $data = json_decode($request->getContent(), true);
182
        $userId = $data['userId'] ?? null;
183
184
        /** @var User $user */
185
        $user = $userRepo->find($userId);
186
        if (!$user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
187
            return $this->json(['error' => 'User not found']);
188
        }
189
190
        $isoCode = $user->getLocale();
191
192
        /** @var Legal $term */
193
        $term = $this->getLastConditionByLanguage($languageRepo, $isoCode, $legalTermsRepo, $settingsManager);
194
195
        if (!$term) {
0 ignored issues
show
introduced by
$term is of type Chamilo\CoreBundle\Entity\Legal, thus it always evaluated to true.
Loading history...
196
            return $this->json(['error' => 'Terms not found']);
197
        }
198
199
        $legalAcceptType = $term->getVersion().':'.$term->getLanguageId().':'.time();
200
        UserManager::update_extra_field_value(
201
            $userId,
202
            'legal_accept',
203
            $legalAcceptType
204
        );
205
206
        $bossList = UserManager::getStudentBossList($userId);
207
        if (!empty($bossList)) {
208
            $bossList = array_column($bossList, 'boss_id');
209
            foreach ($bossList as $bossId) {
210
                $subjectEmail = sprintf(
211
                    $translator->trans('User %s signed the agreement'),
212
                    $user->getFullname()
213
                );
214
                $contentEmail = sprintf(
215
                    $translator->trans('User %s signed the agreement.TheDateY'),
216
                    $user->getFullname(),
217
                    api_get_local_time()
218
                );
219
220
                MessageManager::send_message_simple(
221
                    $bossId,
222
                    $subjectEmail,
223
                    $contentEmail,
224
                    $userId
225
                );
226
            }
227
        }
228
229
        return $this->json([
230
            'success' => true,
231
            'message' => $translator->trans('Terms accepted successfully.'),
232
        ]);
233
    }
234
235
    #[Route('/handle-privacy-request', name: 'chamilo_core_social_handle_privacy_request')]
236
    public function handlePrivacyRequest(
237
        Request $request,
238
        SettingsManager $settingsManager,
239
        UserRepository $userRepo,
240
        TranslatorInterface $translator,
241
        MailerInterface $mailer,
242
        RequestStack $requestStack
243
    ): JsonResponse {
244
        $data = json_decode($request->getContent(), true);
245
        $userId = $data['userId'] ? (int) $data['userId'] : null;
246
        $explanation = $data['explanation'] ?? '';
247
        $requestType = $data['requestType'] ?? '';
248
249
        /** @var User $user */
250
        $user = $userRepo->find($userId);
251
252
        if (!$user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
253
            return $this->json(['success' => false, 'message' => 'User not found']);
254
        }
255
256
        if ('delete_account' === $requestType) {
257
            $fieldToUpdate = 'request_for_delete_account';
258
            $justificationFieldToUpdate = 'request_for_delete_account_justification';
259
            $emailSubject = $translator->trans('Request for account deletion');
260
            $emailContent = sprintf($translator->trans('User %s asked for the deletion of his/her account, explaining that : ').$explanation, $user->getFullName());
261
        } elseif ('delete_legal' === $requestType) {
262
            $fieldToUpdate = 'request_for_legal_agreement_consent_removal';
263
            $justificationFieldToUpdate = 'request_for_legal_agreement_consent_removal_justification';
264
            $emailSubject = $translator->trans('Request for consent withdrawal on legal terms');
265
            $emailContent = sprintf($translator->trans('User %s asked for the removal of his/her consent to our legal terms, explaining that: ').$explanation, $user->getFullName());
266
        } else {
267
            return $this->json(['success' => false, 'message' => 'Invalid action type']);
268
        }
269
270
        UserManager::createDataPrivacyExtraFields();
271
        UserManager::update_extra_field_value($userId, $fieldToUpdate, 1);
272
        UserManager::update_extra_field_value($userId, $justificationFieldToUpdate, $explanation);
273
274
        $request = $requestStack->getCurrentRequest();
275
        $baseUrl = $request->getSchemeAndHttpHost().$request->getBasePath();
276
        $specificPath = '/main/admin/user_list_consent.php';
277
        $link = $baseUrl.$specificPath;
278
        $emailContent .= $translator->trans('Go here : ').'<a href="'.$link.'">'.$link.'</a>';
279
280
        $emailOfficer = $settingsManager->getSetting('profile.data_protection_officer_email');
281
        if (!empty($emailOfficer)) {
282
            $email = new Email();
283
            $email
284
                ->from($user->getEmail())
285
                ->to($emailOfficer)
286
                ->subject($emailSubject)
287
                ->html($emailContent)
288
            ;
289
            $mailer->send($email);
290
        } else {
291
            MessageManager::sendMessageToAllAdminUsers($user->getId(), $emailSubject, $emailContent);
292
        }
293
294
        return $this->json([
295
            'success' => true,
296
            'message' => $translator->trans('Your request has been received.'),
297
        ]);
298
    }
299
300
    #[Route('/groups/{userId}', name: 'chamilo_core_social_groups')]
301
    public function getGroups(
302
        int $userId,
303
        UsergroupRepository $usergroupRepository,
304
        CForumThreadRepository $forumThreadRepository,
305
        SettingsManager $settingsManager,
306
        RequestStack $requestStack
307
    ): JsonResponse {
308
        $baseUrl = $requestStack->getCurrentRequest()->getBaseUrl();
309
        $cid = (int) $settingsManager->getSetting('forum.global_forums_course_id');
310
        $groupsArray = [];
311
        $threadsArray = [];
312
        if (!empty($cid)) {
313
            $threads = $forumThreadRepository->getThreadsBySubscriptions($userId, $cid);
314
            foreach ($threads as $thread) {
315
                $threadId = $thread->getIid();
316
                $forumId = (int) $thread->getForum()->getIid();
317
                $threadsArray[] = [
318
                    'id' => $threadId,
319
                    'name' => $thread->getTitle(),
320
                    'description' => '',
321
                    'url' => $baseUrl.'/main/forum/viewthread.php?cid='.$cid.'&sid=0&gid=0&forum='.$forumId.'&thread='.$threadId,
322
                    'go_to' => $baseUrl.'/main/forum/index.php?cid='.$cid.'&sid=0&gid=0',
323
                ];
324
            }
325
        } else {
326
            $groups = $usergroupRepository->getGroupsByUser($userId);
327
            foreach ($groups as $group) {
328
                $groupsArray[] = [
329
                    'id' => $group->getId(),
330
                    'name' => $group->getTitle(),
331
                    'description' => $group->getDescription(),
332
                    'url' => $baseUrl.'/resources/usergroups/show/'.$group->getId(),
333
                ];
334
            }
335
        }
336
337
        if (!empty($threadsArray)) {
338
            return $this->json(['groups' => $threadsArray]);
339
        }
340
341
        return $this->json(['groups' => $groupsArray]);
342
    }
343
344
    #[Route('/group/{groupId}/discussion/{discussionId}/messages', name: 'chamilo_core_social_group_discussion_messages')]
345
    public function getDiscussionMessages(
346
        $groupId,
347
        $discussionId,
348
        MessageRepository $messageRepository,
349
        UserRepository $userRepository,
350
        MessageAttachmentRepository $attachmentRepository
351
    ): JsonResponse {
352
        $messages = $messageRepository->getMessagesByGroupAndMessage((int) $groupId, (int) $discussionId);
353
354
        $formattedMessages = $this->formatMessagesHierarchy($messages, $userRepository, $attachmentRepository);
355
356
        return $this->json($formattedMessages);
357
    }
358
359
    #[Route('/get-forum-link', name: 'get_forum_link')]
360
    public function getForumLink(
361
        SettingsManager $settingsManager,
362
        RequestStack $requestStack
363
    ): JsonResponse {
364
        $baseUrl = $requestStack->getCurrentRequest()->getBaseUrl();
365
        $cid = (int) $settingsManager->getSetting('forum.global_forums_course_id');
366
367
        $goToLink = '';
368
        if (!empty($cid)) {
369
            $goToLink = $baseUrl.'/main/forum/index.php?cid='.$cid.'&sid=0&gid=0';
370
        }
371
372
        return $this->json(['go_to' => $goToLink]);
373
    }
374
375
    #[Route('/invite-friends/{userId}/{groupId}', name: 'chamilo_core_social_invite_friends')]
376
    public function inviteFriends(
377
        int $userId,
378
        int $groupId,
379
        UserRepository $userRepository,
380
        UsergroupRepository $usergroupRepository,
381
        IllustrationRepository $illustrationRepository
382
    ): JsonResponse {
383
        $user = $userRepository->find($userId);
384
        if (!$user) {
385
            return $this->json(['error' => 'User not found'], Response::HTTP_NOT_FOUND);
386
        }
387
388
        $group = $usergroupRepository->find($groupId);
389
        if (!$group) {
390
            return $this->json(['error' => 'Group not found'], Response::HTTP_NOT_FOUND);
391
        }
392
393
        $friends = $userRepository->getFriendsNotInGroup($userId, $groupId);
394
395
        $friendsList = array_map(function ($friend) use ($illustrationRepository) {
396
            return [
397
                'id' => $friend->getId(),
398
                'name' => $friend->getFirstName().' '.$friend->getLastName(),
399
                'avatar' => $illustrationRepository->getIllustrationUrl($friend),
400
            ];
401
        }, $friends);
402
403
        return $this->json(['friends' => $friendsList]);
404
    }
405
406
    #[Route('/add-users-to-group/{groupId}', name: 'chamilo_core_social_add_users_to_group')]
407
    public function addUsersToGroup(Request $request, int $groupId, UsergroupRepository $usergroupRepository): JsonResponse
408
    {
409
        $data = json_decode($request->getContent(), true);
410
        $userIds = $data['userIds'] ?? [];
411
412
        try {
413
            $usergroupRepository->addUserToGroup($userIds, $groupId);
414
415
            return $this->json(['success' => true, 'message' => 'Users added to group successfully.']);
416
        } catch (Exception $e) {
417
            return $this->json(['success' => false, 'message' => 'An error occurred: '.$e->getMessage()], Response::HTTP_BAD_REQUEST);
418
        }
419
    }
420
421
    #[Route('/group/{groupId}/invited-users', name: 'chamilo_core_social_group_invited_users')]
422
    public function groupInvitedUsers(int $groupId, UsergroupRepository $usergroupRepository, IllustrationRepository $illustrationRepository): JsonResponse
423
    {
424
        $invitedUsers = $usergroupRepository->getInvitedUsersByGroup($groupId);
425
426
        $invitedUsersList = array_map(function ($user) {
427
            return [
428
                'id' => $user['id'],
429
                'name' => $user['username'],
430
                // 'avatar' => $illustrationRepository->getIllustrationUrl($user),
431
            ];
432
        }, $invitedUsers);
433
434
        return $this->json(['invitedUsers' => $invitedUsersList]);
435
    }
436
437
    #[Route('/user-profile/{userId}', name: 'chamilo_core_social_user_profile')]
438
    public function getUserProfile(
439
        int $userId,
440
        SettingsManager $settingsManager,
441
        LanguageRepository $languageRepository,
442
        UserRepository $userRepository,
443
        RequestStack $requestStack,
444
        TrackEOnlineRepository $trackOnlineRepository,
445
        ExtraFieldRepository $extraFieldRepository,
446
        ExtraFieldOptionsRepository $extraFieldOptionsRepository
447
    ): JsonResponse {
448
        $user = $userRepository->find($userId);
449
        if (!$user) {
450
            return $this->json(['error' => 'User not found'], Response::HTTP_NOT_FOUND);
451
        }
452
453
        $baseUrl = $requestStack->getCurrentRequest()->getBaseUrl();
454
        $profileFieldsVisibilityJson = $settingsManager->getSetting('profile.profile_fields_visibility');
455
        $profileFieldsVisibility = json_decode($profileFieldsVisibilityJson, true)['options'] ?? [];
456
457
        $vCardUserLink = $profileFieldsVisibility['vcard'] ?? true ? $baseUrl.'/main/social/vcard_export.php?userId='.(int) $userId : '';
458
459
        $languageInfo = null;
460
        if ($profileFieldsVisibility['language'] ?? true) {
461
            $language = $languageRepository->findByIsoCode($user->getLocale());
462
            if ($language) {
463
                $languageInfo = [
464
                    'label' => $language->getOriginalName(),
465
                    'value' => $language->getEnglishName(),
466
                    'code' => $language->getIsocode(),
467
                ];
468
            }
469
        }
470
471
        $isUserOnline = $trackOnlineRepository->isUserOnline($userId);
472
        $userOnlyInChat = $this->checkUserStatus($userId, $userRepository);
473
        $extraFields = $this->getExtraFieldBlock($userId, $userRepository, $settingsManager, $extraFieldRepository, $extraFieldOptionsRepository);
474
475
        $response = [
476
            'vCardUserLink' => $vCardUserLink,
477
            'language' => $languageInfo,
478
            'visibility' => $profileFieldsVisibility,
479
            'isUserOnline' => $isUserOnline,
480
            'userOnlyInChat' => $userOnlyInChat,
481
            'extraFields' => $extraFields,
482
        ];
483
484
        return $this->json($response);
485
    }
486
487
    private function getExtraFieldBlock(
488
        int $userId,
489
        UserRepository $userRepository,
490
        SettingsManager $settingsManager,
491
        ExtraFieldRepository $extraFieldRepository,
492
        ExtraFieldOptionsRepository $extraFieldOptionsRepository
493
    ): array {
494
        $user = $userRepository->find($userId);
495
        if (!$user) {
496
            return [];
497
        }
498
499
        $fieldVisibilityConfig = $settingsManager->getSetting('profile.profile_fields_visibility');
500
        $fieldVisibility = $fieldVisibilityConfig ? json_decode($fieldVisibilityConfig, true)['options'] : [];
501
502
        $extraUserData = $userRepository->getExtraUserData($userId);
503
        $extraFieldsFormatted = [];
504
        foreach ($extraUserData as $key => $value) {
505
            $fieldVariable = str_replace('extra_', '', $key);
506
507
            $extraField = $extraFieldRepository->getHandlerFieldInfoByFieldVariable($fieldVariable, ExtraField::USER_FIELD_TYPE);
508
            if (!$extraField || !isset($fieldVisibility[$fieldVariable]) || !$fieldVisibility[$fieldVariable]) {
509
                continue;
510
            }
511
512
            $fieldValue = \is_array($value) ? implode(', ', $value) : $value;
513
514
            switch ($extraField['type']) {
515
                case ExtraField::FIELD_TYPE_RADIO:
516
                case ExtraField::FIELD_TYPE_SELECT:
517
                    $extraFieldOptions = $extraFieldOptionsRepository->getFieldOptionByFieldAndOption($extraField['id'], $fieldValue, ExtraField::USER_FIELD_TYPE);
518
                    if (!empty($extraFieldOptions)) {
519
                        $optionTexts = array_map(function ($option) {
520
                            return $option['display_text'];
521
                        }, $extraFieldOptions);
522
                        $fieldValue = implode(', ', $optionTexts);
523
                    }
524
525
                    break;
526
527
                case ExtraField::FIELD_TYPE_GEOLOCALIZATION_COORDINATES:
528
                case ExtraField::FIELD_TYPE_GEOLOCALIZATION:
529
                    $geoData = explode('::', $fieldValue);
530
                    $locationName = $geoData[0];
531
                    $coordinates = $geoData[1] ?? '';
532
                    $fieldValue = $locationName;
533
534
                    break;
535
            }
536
537
            $extraFieldsFormatted[] = [
538
                'variable' => $fieldVariable,
539
                'label' => $extraField['display_text'],
540
                'value' => $fieldValue,
541
            ];
542
        }
543
544
        return $extraFieldsFormatted;
545
    }
546
547
    #[Route('/invitations/{userId}', name: 'chamilo_core_social_invitations')]
548
    public function getInvitations(
549
        int $userId,
550
        MessageRepository $messageRepository,
551
        UsergroupRepository $usergroupRepository,
552
        UserRepository $userRepository,
553
        TranslatorInterface $translator
554
    ): JsonResponse {
555
        $user = $this->getUser();
556
        if ($userId !== $user->getId()) {
557
            return $this->json(['error' => 'Unauthorized'], Response::HTTP_UNAUTHORIZED);
558
        }
559
560
        $receivedMessages = $messageRepository->findReceivedInvitationsByUser($user);
561
        $receivedInvitations = [];
562
        foreach ($receivedMessages as $message) {
563
            $sender = $message->getSender();
564
            $receivedInvitations[] = [
565
                'id' => $message->getId(),
566
                'itemId' => $sender->getId(),
567
                'itemName' => $sender->getFirstName().' '.$sender->getLastName(),
568
                'itemPicture' => $userRepository->getUserPicture($sender->getId()),
569
                'content' => $message->getContent(),
570
                'date' => $message->getSendDate()->format('Y-m-d H:i:s'),
571
                'canAccept' => true,
572
                'canDeny' => true,
573
            ];
574
        }
575
576
        $sentMessages = $messageRepository->findSentInvitationsByUser($user);
577
        $sentInvitations = [];
578
        foreach ($sentMessages as $message) {
579
            foreach ($message->getReceivers() as $receiver) {
580
                $receiverUser = $receiver->getReceiver();
581
                $sentInvitations[] = [
582
                    'id' => $message->getId(),
583
                    'itemId' => $receiverUser->getId(),
584
                    'itemName' => $receiverUser->getFirstName().' '.$receiverUser->getLastName(),
585
                    'itemPicture' => $userRepository->getUserPicture($receiverUser->getId()),
586
                    'content' => $message->getContent(),
587
                    'date' => $message->getSendDate()->format('Y-m-d H:i:s'),
588
                    'canAccept' => false,
589
                    'canDeny' => false,
590
                ];
591
            }
592
        }
593
594
        $pendingGroupInvitations = [];
595
        $pendingGroups = $usergroupRepository->getGroupsByUser($userId, Usergroup::GROUP_USER_PERMISSION_PENDING_INVITATION);
596
597
        /** @var Usergroup $group */
598
        foreach ($pendingGroups as $group) {
599
            $isGroupVisible = 1 === (int) $group->getVisibility();
600
            $infoVisibility = !$isGroupVisible ? ' - '.$translator->trans('This group is closed.') : '';
601
            $pendingGroupInvitations[] = [
602
                'id' => $group->getId(),
603
                'itemId' => $group->getId(),
604
                'itemName' => $group->getTitle().$infoVisibility,
605
                'itemPicture' => $usergroupRepository->getUsergroupPicture($group->getId()),
606
                'content' => $group->getDescription(),
607
                'date' => $group->getCreatedAt()->format('Y-m-d H:i:s'),
608
                'canAccept' => $isGroupVisible,
609
                'canDeny' => true,
610
            ];
611
        }
612
613
        return $this->json([
614
            'receivedInvitations' => $receivedInvitations,
615
            'sentInvitations' => $sentInvitations,
616
            'pendingGroupInvitations' => $pendingGroupInvitations,
617
        ]);
618
    }
619
620
    #[Route('/invitations/count/{userId}', name: 'chamilo_core_social_invitations_count')]
621
    public function getInvitationsCount(
622
        int $userId,
623
        MessageRepository $messageRepository,
624
        UsergroupRepository $usergroupRepository
625
    ): JsonResponse {
626
        $user = $this->getUser();
627
        if ($userId !== $user->getId()) {
628
            return $this->json(['error' => 'Unauthorized']);
629
        }
630
631
        $receivedMessagesCount = \count($messageRepository->findReceivedInvitationsByUser($user));
632
        $pendingGroupInvitationsCount = \count($usergroupRepository->getGroupsByUser($userId, Usergroup::GROUP_USER_PERMISSION_PENDING_INVITATION));
633
        $totalInvitationsCount = $receivedMessagesCount + $pendingGroupInvitationsCount;
634
635
        return $this->json(['totalInvitationsCount' => $totalInvitationsCount]);
636
    }
637
638
    #[Route('/search', name: 'chamilo_core_social_search')]
639
    public function search(
640
        Request $request,
641
        UserRepository $userRepository,
642
        UsergroupRepository $usergroupRepository,
643
        TrackEOnlineRepository $trackOnlineRepository,
644
        MessageRepository $messageRepository
645
    ): JsonResponse {
646
        $query = $request->query->get('query', '');
647
        $type = $request->query->get('type', 'user');
648
        $from = $request->query->getInt('from', 0);
649
        $numberOfItems = $request->query->getInt('number_of_items', 1000);
650
651
        $formattedResults = [];
652
        if ('user' === $type) {
653
            /** @var User $user */
654
            $user = $this->getUser();
655
            $results = $userRepository->searchUsersByTags($query, $user->getId(), 0, $from, $numberOfItems);
656
            foreach ($results as $item) {
657
                $isUserOnline = $trackOnlineRepository->isUserOnline($item['id']);
658
                $relation = $userRepository->getUserRelationWithType($user->getId(), $item['id']);
659
                $userReceiver = $userRepository->find($item['id']);
660
                $existingInvitations = $messageRepository->existingInvitations($user, $userReceiver);
661
                $formattedResults[] = [
662
                    'id' => $item['id'],
663
                    'name' => $item['firstname'].' '.$item['lastname'],
664
                    'avatar' => $userRepository->getUserPicture($item['id']),
665
                    'role' => 5 === $item['status'] ? 'student' : 'teacher',
666
                    'status' => $isUserOnline ? 'online' : 'offline',
667
                    'url' => '/social?id='.$item['id'],
668
                    'relationType' => $relation['relationType'] ?? null,
669
                    'existingInvitations' => $existingInvitations,
670
                ];
671
            }
672
        } elseif ('group' === $type) {
673
            // Perform group search
674
            $results = $usergroupRepository->searchGroupsByTags($query, $from, $numberOfItems);
675
            foreach ($results as $item) {
676
                $formattedResults[] = [
677
                    'id' => $item['id'],
678
                    'name' => $item['title'],
679
                    'description' => $item['description'] ?? '',
680
                    'image' => $usergroupRepository->getUsergroupPicture($item['id']),
681
                    'url' => '/resources/usergroups/show/'.$item['id'],
682
                ];
683
            }
684
        }
685
686
        return $this->json(['results' => $formattedResults]);
687
    }
688
689
    #[Route('/group-details/{groupId}', name: 'chamilo_core_social_group_details')]
690
    public function groupDetails(
691
        int $groupId,
692
        UsergroupRepository $usergroupRepository,
693
        TrackEOnlineRepository $trackOnlineRepository
694
    ): JsonResponse {
695
        /** @var User $user */
696
        $user = $this->getUser();
697
        if (!$user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
698
            return $this->json(['error' => 'User not authenticated'], Response::HTTP_UNAUTHORIZED);
699
        }
700
701
        /** @var Usergroup $group */
702
        $group = $usergroupRepository->find($groupId);
703
        if (!$group) {
0 ignored issues
show
introduced by
$group is of type Chamilo\CoreBundle\Entity\Usergroup, thus it always evaluated to true.
Loading history...
704
            return $this->json(['error' => 'Group not found'], Response::HTTP_NOT_FOUND);
705
        }
706
707
        $isMember = $usergroupRepository->isGroupMember($groupId, $user);
708
        $role = $usergroupRepository->getUserGroupRole($groupId, $user->getId());
709
        $isUserOnline = $trackOnlineRepository->isUserOnline($user->getId());
710
        $isModerator = $usergroupRepository->isGroupModerator($groupId, $user->getId());
711
712
        $groupDetails = [
713
            'id' => $group->getId(),
714
            'title' => $group->getTitle(),
715
            'description' => $group->getDescription(),
716
            'url' => $group->getUrl(),
717
            'image' => $usergroupRepository->getUsergroupPicture($group->getId()),
718
            'visibility' => (int) $group->getVisibility(),
719
            'allowMembersToLeaveGroup' => $group->getAllowMembersToLeaveGroup(),
720
            'isMember' => $isMember,
721
            'isModerator' => $isModerator,
722
            'role' => $role,
723
            'isUserOnline' => $isUserOnline,
724
            'isAllowedToLeave' => 1 === $group->getAllowMembersToLeaveGroup(),
725
        ];
726
727
        return $this->json($groupDetails);
728
    }
729
730
    #[Route('/group-action', name: 'chamilo_core_social_group_action')]
731
    public function group(
732
        Request $request,
733
        UsergroupRepository $usergroupRepository,
734
        EntityManagerInterface $em,
735
        MessageRepository $messageRepository
736
    ): JsonResponse {
737
        if (str_starts_with($request->headers->get('Content-Type'), 'multipart/form-data')) {
738
            $userId = $request->request->get('userId');
739
            $groupId = $request->request->get('groupId');
740
            $action = $request->request->get('action');
741
            $title = $request->request->get('title', '');
742
            $content = $request->request->get('content', '');
743
            $parentId = $request->request->get('parentId', 0);
744
            $editMessageId = $request->request->get('messageId', 0);
745
746
            $structuredFiles = [];
747
            if ($request->files->has('files')) {
748
                $files = $request->files->get('files');
749
                foreach ($files as $file) {
750
                    $structuredFiles[] = [
751
                        'name' => $file->getClientOriginalName(),
752
                        'full_path' => $file->getRealPath(),
753
                        'type' => $file->getMimeType(),
754
                        'tmp_name' => $file->getPathname(),
755
                        'error' => $file->getError(),
756
                        'size' => $file->getSize(),
757
                    ];
758
                }
759
            }
760
        } else {
761
            $data = json_decode($request->getContent(), true);
762
            $userId = $data['userId'] ?? null;
763
            $groupId = $data['groupId'] ?? null;
764
            $action = $data['action'] ?? null;
765
        }
766
767
        if (!$userId || !$groupId || !$action) {
768
            return $this->json(['error' => 'Missing parameters'], Response::HTTP_BAD_REQUEST);
769
        }
770
771
        try {
772
            switch ($action) {
773
                case 'accept':
774
                    $userRole = $usergroupRepository->getUserGroupRole($groupId, $userId);
775
                    if (\in_array(
776
                        $userRole,
777
                        [
778
                            Usergroup::GROUP_USER_PERMISSION_PENDING_INVITATION_SENT_BY_USER,
779
                            Usergroup::GROUP_USER_PERMISSION_PENDING_INVITATION,
780
                        ]
781
                    )) {
782
                        $usergroupRepository->updateUserRole($userId, $groupId, Usergroup::GROUP_USER_PERMISSION_READER);
783
                    }
784
785
                    break;
786
787
                case 'join':
788
                    $usergroupRepository->addUserToGroup($userId, $groupId);
789
790
                    break;
791
792
                case 'deny':
793
                    $usergroupRepository->removeUserFromGroup($userId, $groupId, false);
794
795
                    break;
796
797
                case 'leave':
798
                    $usergroupRepository->removeUserFromGroup($userId, $groupId);
799
800
                    break;
801
802
                case 'reply_message_group':
803
                    $title = $title ?: substr(strip_tags($content), 0, 50);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $title does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $content does not seem to be defined for all execution paths leading up to this point.
Loading history...
804
805
                    // no break
806
                case 'edit_message_group':
807
                case 'add_message_group':
808
                    $res = MessageManager::send_message(
809
                        $userId,
810
                        $title,
811
                        $content,
812
                        $structuredFiles,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $structuredFiles does not seem to be defined for all execution paths leading up to this point.
Loading history...
813
                        [],
814
                        $groupId,
815
                        $parentId,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $parentId does not seem to be defined for all execution paths leading up to this point.
Loading history...
816
                        $editMessageId,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $editMessageId does not seem to be defined for all execution paths leading up to this point.
Loading history...
817
                        0,
818
                        $userId,
819
                        false,
820
                        0,
821
                        false,
822
                        false,
823
                        Message::MESSAGE_TYPE_GROUP
824
                    );
825
826
                    break;
827
828
                case 'delete_message_group':
829
                    $messageId = $data['messageId'] ?? null;
830
831
                    if (!$messageId) {
832
                        return $this->json(['error' => 'Missing messageId parameter'], Response::HTTP_BAD_REQUEST);
833
                    }
834
835
                    $messageRepository->deleteTopicAndChildren($groupId, $messageId);
836
837
                    break;
838
839
                default:
840
                    return $this->json(['error' => 'Invalid action'], Response::HTTP_BAD_REQUEST);
841
            }
842
843
            $em->flush();
844
845
            return $this->json(['success' => 'Action completed successfully']);
846
        } catch (Exception $e) {
847
            return $this->json(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
848
        }
849
    }
850
851
    #[Route('/user-action', name: 'chamilo_core_social_user_action')]
852
    public function user(
853
        Request $request,
854
        UserRepository $userRepository,
855
        MessageRepository $messageRepository,
856
        EntityManagerInterface $em
857
    ): JsonResponse {
858
        $data = json_decode($request->getContent(), true);
859
860
        $userId = $data['userId'] ?? null;
861
        $targetUserId = $data['targetUserId'] ?? null;
862
        $action = $data['action'] ?? null;
863
        $isMyFriend = $data['is_my_friend'] ?? false;
864
        $subject = $data['subject'] ?? '';
865
        $content = $data['content'] ?? '';
866
867
        if (!$userId || !$targetUserId || !$action) {
868
            return $this->json(['error' => 'Missing parameters']);
869
        }
870
871
        $currentUser = $userRepository->find($userId);
872
        $friendUser = $userRepository->find($targetUserId);
873
874
        if (null === $currentUser || null === $friendUser) {
875
            return $this->json(['error' => 'User not found']);
876
        }
877
878
        try {
879
            switch ($action) {
880
                case 'send_invitation':
881
                    $result = $messageRepository->sendInvitationToFriend($currentUser, $friendUser, $subject, $content);
882
                    if (!$result) {
883
                        return $this->json(['error' => 'Invitation already exists or could not be sent']);
884
                    }
885
886
                    break;
887
888
                case 'send_message':
889
                    $result = MessageManager::send_message($friendUser->getId(), $subject, $content);
890
891
                    break;
892
893
                case 'add_friend':
894
                    $relationType = $isMyFriend ? UserRelUser::USER_RELATION_TYPE_FRIEND : UserRelUser::USER_UNKNOWN;
895
896
                    $userRepository->relateUsers($currentUser, $friendUser, $relationType);
897
                    $userRepository->relateUsers($friendUser, $currentUser, $relationType);
898
899
                    $messageRepository->invitationAccepted($friendUser, $currentUser);
900
901
                    break;
902
903
                case 'deny_friend':
904
                    $messageRepository->invitationDenied($friendUser, $currentUser);
905
906
                    break;
907
908
                default:
909
                    return $this->json(['error' => 'Invalid action']);
910
            }
911
912
            $em->flush();
913
914
            return $this->json(['success' => 'Action completed successfully']);
915
        } catch (Exception $e) {
916
            return $this->json(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
917
        }
918
    }
919
920
    #[Route('/user-relation/{currentUserId}/{profileUserId}', name: 'chamilo_core_social_get_user_relation')]
921
    public function getUserRelation(int $currentUserId, int $profileUserId, EntityManagerInterface $em): JsonResponse
922
    {
923
        $isAllowed = $this->checkUserRelationship($currentUserId, $profileUserId, $em);
924
925
        return $this->json([
926
            'isAllowed' => $isAllowed,
927
        ]);
928
    }
929
930
    #[Route('/online-status', name: 'chamilo_core_social_get_online_status', methods: ['POST'])]
931
    public function getOnlineStatus(Request $request, TrackEOnlineRepository $trackOnlineRepository): JsonResponse
932
    {
933
        $data = json_decode($request->getContent(), true);
934
        $userIds = $data['userIds'] ?? [];
935
936
        $onlineStatuses = [];
937
        foreach ($userIds as $userId) {
938
            $onlineStatuses[$userId] = $trackOnlineRepository->isUserOnline($userId);
939
        }
940
941
        return $this->json($onlineStatuses);
942
    }
943
944
    #[Route('/upload-group-picture/{groupId}', name: 'chamilo_core_social_upload_group_picture')]
945
    public function uploadGroupPicture(
946
        Request $request,
947
        int $groupId,
948
        UsergroupRepository $usergroupRepository,
949
        IllustrationRepository $illustrationRepository
950
    ): JsonResponse {
951
        $file = $request->files->get('picture');
952
        if ($file instanceof UploadedFile) {
953
            $userGroup = $usergroupRepository->find($groupId);
954
            $illustrationRepository->addIllustration($userGroup, $this->getUser(), $file);
955
        }
956
957
        return new JsonResponse(['success' => 'Group and image saved successfully'], Response::HTTP_OK);
958
    }
959
960
    /**
961
     * Formats a hierarchical structure of messages for display.
962
     *
963
     * This function takes an array of Message entities and recursively formats them into a hierarchical structure.
964
     * Each message is formatted with details such as user information, creation date, content, and attachments.
965
     * The function also assigns a level to each message based on its depth in the hierarchy for display purposes.
966
     */
967
    private function formatMessagesHierarchy(array $messages, UserRepository $userRepository, MessageAttachmentRepository $attachmentRepository, ?int $parentId = null, int $level = 0): array
968
    {
969
        $formattedMessages = [];
970
971
        /** @var Message $message */
972
        foreach ($messages as $message) {
973
            if (($message->getParent() ? $message->getParent()->getId() : null) === $parentId) {
974
                $attachments = $message->getAttachments();
975
                $attachmentsUrls = [];
976
                $attachmentSize = 0;
977
                if ($attachments) {
978
                    /** @var MessageAttachment $attachment */
979
                    foreach ($attachments as $attachment) {
980
                        $attachmentsUrls[] = [
981
                            'link' => $attachmentRepository->getResourceFileDownloadUrl($attachment),
982
                            'filename' => $attachment->getFilename(),
983
                            'size' => $attachment->getSize(),
984
                        ];
985
                        $attachmentSize += $attachment->getSize();
986
                    }
987
                }
988
                $formattedMessage = [
989
                    'id' => $message->getId(),
990
                    'user' => $message->getSender()->getFullName(),
991
                    'created' => $message->getSendDate()->format(DateTimeInterface::ATOM),
992
                    'title' => $message->getTitle(),
993
                    'content' => $message->getContent(),
994
                    'parentId' => $message->getParent() ? $message->getParent()->getId() : null,
995
                    'avatar' => $userRepository->getUserPicture($message->getSender()->getId()),
996
                    'senderId' => $message->getSender()->getId(),
997
                    'attachment' => $attachmentsUrls ?? null,
998
                    'attachmentSize' => $attachmentSize > 0 ? $attachmentSize : null,
999
                    'level' => $level,
1000
                ];
1001
1002
                $children = $this->formatMessagesHierarchy($messages, $userRepository, $attachmentRepository, $message->getId(), $level + 1);
1003
                if (!empty($children)) {
1004
                    $formattedMessage['children'] = $children;
1005
                }
1006
1007
                $formattedMessages[] = $formattedMessage;
1008
            }
1009
        }
1010
1011
        return $formattedMessages;
1012
    }
1013
1014
    /**
1015
     * Checks the relationship between the current user and another user.
1016
     *
1017
     * This method first checks for a direct relationship between the two users. If no direct relationship is found,
1018
     * it then checks for indirect relationships through common friends (friends of friends).
1019
     */
1020
    private function checkUserRelationship(int $currentUserId, int $otherUserId, EntityManagerInterface $em): bool
1021
    {
1022
        if ($currentUserId === $otherUserId) {
1023
            return true;
1024
        }
1025
1026
        $relation = $em->getRepository(UserRelUser::class)
1027
            ->findOneBy([
1028
                'relationType' => [
1029
                    UserRelUser::USER_RELATION_TYPE_FRIEND,
1030
                    UserRelUser::USER_RELATION_TYPE_GOODFRIEND,
1031
                ],
1032
                'friend' => $otherUserId,
1033
                'user' => $currentUserId,
1034
            ])
1035
        ;
1036
1037
        if (null !== $relation) {
1038
            return true;
1039
        }
1040
1041
        $friendsOfCurrentUser = $em->getRepository(UserRelUser::class)
1042
            ->findBy([
1043
                'relationType' => [
1044
                    UserRelUser::USER_RELATION_TYPE_FRIEND,
1045
                    UserRelUser::USER_RELATION_TYPE_GOODFRIEND,
1046
                ],
1047
                'user' => $currentUserId,
1048
            ])
1049
        ;
1050
1051
        foreach ($friendsOfCurrentUser as $friendRelation) {
1052
            $friendId = $friendRelation->getFriend()->getId();
1053
            $relationThroughFriend = $em->getRepository(UserRelUser::class)
1054
                ->findOneBy([
1055
                    'relationType' => [
1056
                        UserRelUser::USER_RELATION_TYPE_FRIEND,
1057
                        UserRelUser::USER_RELATION_TYPE_GOODFRIEND,
1058
                    ],
1059
                    'friend' => $otherUserId,
1060
                    'user' => $friendId,
1061
                ])
1062
            ;
1063
1064
            if (null !== $relationThroughFriend) {
1065
                return true;
1066
            }
1067
        }
1068
1069
        return false;
1070
    }
1071
1072
    /**
1073
     * Checks the chat status of a user based on their user ID. It verifies if the user's chat status
1074
     * is active (indicated by a status of 1).
1075
     */
1076
    private function checkUserStatus(int $userId, UserRepository $userRepository): bool
1077
    {
1078
        $userStatus = $userRepository->getExtraUserDataByField($userId, 'user_chat_status');
1079
1080
        return !empty($userStatus) && isset($userStatus['user_chat_status']) && 1 === (int) $userStatus['user_chat_status'];
1081
    }
1082
1083
    /**
1084
     * Retrieves the most recent legal terms for a specified language. If no terms are found for the given language,
1085
     * the function attempts to retrieve terms for the platform's default language. If terms are still not found,
1086
     * it defaults to English ('en_US').
1087
     */
1088
    private function getLastConditionByLanguage(LanguageRepository $languageRepo, string $isoCode, LegalRepository $legalTermsRepo, SettingsManager $settingsManager): ?Legal
1089
    {
1090
        $language = $languageRepo->findByIsoCode($isoCode);
1091
        $languageId = (int) $language->getId();
1092
        $term = $legalTermsRepo->getLastConditionByLanguage($languageId);
1093
        if (!$term) {
1094
            $defaultLanguage = $settingsManager->getSetting('language.platform_language');
1095
            $language = $languageRepo->findByIsoCode($defaultLanguage);
1096
            $languageId = (int) $language->getId();
1097
            $term = $legalTermsRepo->getLastConditionByLanguage((int) $languageId);
1098
            if (!$term) {
1099
                $language = $languageRepo->findByIsoCode('en_US');
1100
                $languageId = (int) $language->getId();
1101
                $term = $legalTermsRepo->getLastConditionByLanguage((int) $languageId);
1102
            }
1103
        }
1104
1105
        return $term;
1106
    }
1107
}
1108