Passed
Push — master ( 7f4fc5...0155f0 )
by
unknown
16:02 queued 08:04
created

SocialController::formatMessagesHierarchy()   B

Complexity

Conditions 9
Paths 18

Size

Total Lines 45
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

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