Passed
Pull Request — master (#7182)
by
unknown
10:12
created

SocialController::getUserProfile()   B

Complexity

Conditions 7
Paths 25

Size

Total Lines 52
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 30
nc 25
nop 8
dl 0
loc 52
rs 8.5066
c 0
b 0
f 0

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1088
                    $blockButton = true;
1089
                    $infoMessage .= $translator->trans('You must first fill your profile to enable the terms and conditions validation.');
1090
                }
1091
            }
1092
        }
1093
1094
        return $this->json([
1095
            'blockButton' => $blockButton,
1096
            'infoMessage' => $infoMessage,
1097
        ]);
1098
    }
1099
1100
    /**
1101
     * Formats a hierarchical structure of messages for display.
1102
     *
1103
     * This function takes an array of Message entities and recursively formats them into a hierarchical structure.
1104
     * Each message is formatted with details such as user information, creation date, content, and attachments.
1105
     * The function also assigns a level to each message based on its depth in the hierarchy for display purposes.
1106
     */
1107
    private function formatMessagesHierarchy(array $messages, UserRepository $userRepository, MessageAttachmentRepository $attachmentRepository, ?int $parentId = null, int $level = 0): array
1108
    {
1109
        $formattedMessages = [];
1110
1111
        /** @var Message $message */
1112
        foreach ($messages as $message) {
1113
            if (($message->getParent() ? $message->getParent()->getId() : null) === $parentId) {
1114
                $attachments = $message->getAttachments();
1115
                $attachmentsUrls = [];
1116
                $attachmentSize = 0;
1117
                if ($attachments) {
1118
                    /** @var MessageAttachment $attachment */
1119
                    foreach ($attachments as $attachment) {
1120
                        $attachmentsUrls[] = [
1121
                            'link' => $attachmentRepository->getResourceFileDownloadUrl($attachment),
1122
                            'filename' => $attachment->getFilename(),
1123
                            'size' => $attachment->getSize(),
1124
                        ];
1125
                        $attachmentSize += $attachment->getSize();
1126
                    }
1127
                }
1128
                $formattedMessage = [
1129
                    'id' => $message->getId(),
1130
                    'user' => $message->getSender()->getFullName(),
1131
                    'created' => $message->getSendDate()->format(DateTimeInterface::ATOM),
1132
                    'title' => $message->getTitle(),
1133
                    'content' => $message->getContent(),
1134
                    'parentId' => $message->getParent() ? $message->getParent()->getId() : null,
1135
                    'avatar' => $userRepository->getUserPicture($message->getSender()->getId()),
1136
                    'senderId' => $message->getSender()->getId(),
1137
                    'attachment' => $attachmentsUrls ?? null,
1138
                    'attachmentSize' => $attachmentSize > 0 ? $attachmentSize : null,
1139
                    'level' => $level,
1140
                ];
1141
1142
                $children = $this->formatMessagesHierarchy($messages, $userRepository, $attachmentRepository, $message->getId(), $level + 1);
1143
                if (!empty($children)) {
1144
                    $formattedMessage['children'] = $children;
1145
                }
1146
1147
                $formattedMessages[] = $formattedMessage;
1148
            }
1149
        }
1150
1151
        return $formattedMessages;
1152
    }
1153
1154
    /**
1155
     * Checks the relationship between the current user and another user.
1156
     *
1157
     * This method first checks for a direct relationship between the two users. If no direct relationship is found,
1158
     * it then checks for indirect relationships through common friends (friends of friends).
1159
     */
1160
    private function checkUserRelationship(int $currentUserId, int $otherUserId, EntityManagerInterface $em): bool
1161
    {
1162
        if ($currentUserId === $otherUserId) {
1163
            return true;
1164
        }
1165
1166
        $relation = $em->getRepository(UserRelUser::class)
1167
            ->findOneBy([
1168
                'relationType' => [
1169
                    UserRelUser::USER_RELATION_TYPE_FRIEND,
1170
                    UserRelUser::USER_RELATION_TYPE_GOODFRIEND,
1171
                ],
1172
                'friend' => $otherUserId,
1173
                'user' => $currentUserId,
1174
            ])
1175
        ;
1176
1177
        if (null !== $relation) {
1178
            return true;
1179
        }
1180
1181
        $friendsOfCurrentUser = $em->getRepository(UserRelUser::class)
1182
            ->findBy([
1183
                'relationType' => [
1184
                    UserRelUser::USER_RELATION_TYPE_FRIEND,
1185
                    UserRelUser::USER_RELATION_TYPE_GOODFRIEND,
1186
                ],
1187
                'user' => $currentUserId,
1188
            ])
1189
        ;
1190
1191
        foreach ($friendsOfCurrentUser as $friendRelation) {
1192
            $friendId = $friendRelation->getFriend()->getId();
1193
            $relationThroughFriend = $em->getRepository(UserRelUser::class)
1194
                ->findOneBy([
1195
                    'relationType' => [
1196
                        UserRelUser::USER_RELATION_TYPE_FRIEND,
1197
                        UserRelUser::USER_RELATION_TYPE_GOODFRIEND,
1198
                    ],
1199
                    'friend' => $otherUserId,
1200
                    'user' => $friendId,
1201
                ])
1202
            ;
1203
1204
            if (null !== $relationThroughFriend) {
1205
                return true;
1206
            }
1207
        }
1208
1209
        return false;
1210
    }
1211
1212
    /**
1213
     * Checks the chat status of a user based on their user ID. It verifies if the user's chat status
1214
     * is active (indicated by a status of 1).
1215
     */
1216
    private function checkUserStatus(int $userId, UserRepository $userRepository): bool
1217
    {
1218
        $userStatus = $userRepository->getExtraUserDataByField($userId, 'user_chat_status');
1219
1220
        return !empty($userStatus) && isset($userStatus['user_chat_status']) && 1 === (int) $userStatus['user_chat_status'];
1221
    }
1222
1223
    /**
1224
     * Retrieves the most recent legal terms for a specified language. If no terms are found for the given language,
1225
     * the function attempts to retrieve terms for the platform's default language. If terms are still not found,
1226
     * it defaults to English ('en_US').
1227
     */
1228
    private function getLastConditionByLanguage(LanguageRepository $languageRepo, string $isoCode, LegalRepository $legalTermsRepo, SettingsManager $settingsManager): ?Legal
1229
    {
1230
        $language = $languageRepo->findByIsoCode($isoCode);
1231
        $languageId = (int) $language->getId();
1232
        $term = $legalTermsRepo->getLastConditionByLanguage($languageId);
1233
        if (!$term) {
1234
            $defaultLanguage = $settingsManager->getSetting('language.platform_language');
1235
            $language = $languageRepo->findByIsoCode($defaultLanguage);
1236
            $languageId = (int) $language->getId();
1237
            $term = $legalTermsRepo->getLastConditionByLanguage((int) $languageId);
1238
            if (!$term) {
1239
                $language = $languageRepo->findByIsoCode('en_US');
1240
                $languageId = (int) $language->getId();
1241
                $term = $legalTermsRepo->getLastConditionByLanguage((int) $languageId);
1242
            }
1243
        }
1244
1245
        return $term;
1246
    }
1247
1248
    private function resolveLegalLanguageId(
1249
        LanguageRepository $languageRepo,
1250
        string $isoCode,
1251
        SettingsManager $settingsManager
1252
    ): int {
1253
        $candidates = [
1254
            $isoCode,
1255
            (string) $settingsManager->getSetting('language.platform_language'),
1256
            'en_US',
1257
        ];
1258
1259
        foreach ($candidates as $code) {
1260
            if ('' === trim((string) $code)) {
1261
                continue;
1262
            }
1263
1264
            $language = $languageRepo->findByIsoCode($code);
1265
            if ($language) {
1266
                return (int) $language->getId();
1267
            }
1268
        }
1269
1270
        return 0;
1271
    }
1272
1273
    private function resolveLatestTermsVersion(
1274
        string $isoCode,
1275
        LanguageRepository $languageRepo,
1276
        LegalRepository $legalRepo,
1277
        SettingsManager $settingsManager
1278
    ): ?array {
1279
        $candidates = [];
1280
1281
        $lang = $languageRepo->findByIsoCode($isoCode);
1282
        if ($lang) {
1283
            $candidates[] = (int) $lang->getId();
1284
        }
1285
1286
        $platformIso = (string) $settingsManager->getSetting('language.platform_language');
1287
        if ($platformIso !== '') {
1288
            $platformLang = $languageRepo->findByIsoCode($platformIso);
1289
            if ($platformLang) {
1290
                $candidates[] = (int) $platformLang->getId();
1291
            }
1292
        }
1293
1294
        $en = $languageRepo->findByIsoCode('en_US');
1295
        if ($en) {
1296
            $candidates[] = (int) $en->getId();
1297
        }
1298
1299
        $candidates = array_values(array_unique(array_filter($candidates)));
1300
1301
        foreach ($candidates as $languageId) {
1302
            $version = $legalRepo->findLatestVersionByLanguage($languageId);
1303
            if ($version > 0) {
1304
                return ['languageId' => $languageId, 'version' => $version];
1305
            }
1306
        }
1307
1308
        return null;
1309
    }
1310
}
1311