Passed
Pull Request — master (#5329)
by Angel Fernando Quiroz
06:46
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\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