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

SocialController::getUserProfile()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 48
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 27
nc 7
nop 8
dl 0
loc 48
rs 9.1768
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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