Passed
Pull Request — master (#6720)
by
unknown
09:43
created

MessageHelper::sendMessage()   C

Complexity

Conditions 12
Paths 34

Size

Total Lines 72
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 35
nc 34
nop 13
dl 0
loc 72
rs 6.9666
c 0
b 0
f 0

How to fix   Long Method    Complexity    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
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\Helpers;
8
9
use Chamilo\CoreBundle\Entity\Message;
10
use Chamilo\CoreBundle\Entity\MessageAttachment;
11
use Chamilo\CoreBundle\Entity\MessageRelUser;
12
use Chamilo\CoreBundle\Entity\User;
13
use Chamilo\CoreBundle\Entity\Usergroup;
14
use Chamilo\CoreBundle\Repository\MessageRepository;
15
use Chamilo\CoreBundle\Repository\Node\UserRepository;
16
use Chamilo\CoreBundle\Settings\SettingsManager;
17
use Doctrine\ORM\EntityManagerInterface;
18
use Exception;
19
use Symfony\Component\HttpFoundation\File\UploadedFile;
20
use Symfony\Component\HttpFoundation\RequestStack;
21
use Symfony\Component\HttpFoundation\Session\SessionInterface;
22
use Symfony\Component\Mailer\MailerInterface;
23
use Symfony\Component\Mime\Address;
24
use Symfony\Component\Mime\Email;
25
26
use const PHP_SAPI;
27
use const UPLOAD_ERR_OK;
28
29
class MessageHelper
30
{
31
    private ?SessionInterface $session = null;
32
33
    public function __construct(
34
        private readonly EntityManagerInterface $entityManager,
35
        private readonly MessageRepository $messageRepository,
36
        private readonly UserRepository $userRepository,
37
        private readonly RequestStack $requestStack,
38
        private readonly AccessUrlHelper $accessUrlHelper,
39
        private readonly SettingsManager $settingsManager,
40
        private readonly MailerInterface $mailer
41
    ) {
42
        if (PHP_SAPI !== 'cli') {
43
            $this->session = $this->requestStack->getSession();
44
        }
45
    }
46
47
    /**
48
     * Sends a simple message with optional attachments and notifications to HR users.
49
     */
50
    public function sendMessageSimple(
51
        int $receiverUserId,
52
        string $subject,
53
        string $message,
54
        int $senderId = 0,
55
        bool $sendCopyToDrhUsers = false,
56
        bool $uploadFiles = true,
57
        array $attachmentList = []
58
    ): ?int {
59
        $files = $_FILES ?: [];
60
        if (false === $uploadFiles) {
61
            $files = [];
62
        }
63
64
        if (!empty($attachmentList)) {
65
            $files = $attachmentList;
66
        }
67
68
        $result = $this->sendMessage(
69
            $receiverUserId,
70
            $subject,
71
            $message,
72
            $files,
73
            [],
74
            0,
75
            0,
76
            0,
77
            $senderId
78
        );
79
80
        if ($sendCopyToDrhUsers) {
81
            $accessUrl = $this->accessUrlHelper->getCurrent();
82
            if (null !== $accessUrl) {
83
                $drhList = $this->userRepository->getDrhListFromUser($receiverUserId, $accessUrl->getId());
84
                if (!empty($drhList)) {
85
                    $receiverInfo = $this->userRepository->find($receiverUserId);
86
87
                    foreach ($drhList as $drhUser) {
88
                        $drhMessage = \sprintf(
89
                            'Copy of message sent to %s',
90
                            $receiverInfo->getFirstname().' '.$receiverInfo->getLastname()
91
                        ).' <br />'.$message;
92
93
                        $this->sendMessageSimple(
94
                            $drhUser->getId(),
95
                            $subject,
96
                            $drhMessage,
97
                            $senderId
98
                        );
99
                    }
100
                }
101
            }
102
        }
103
104
        return $result;
105
    }
106
107
    /**
108
     * Sends a message with attachments, forwards, and additional settings.
109
     */
110
    public function sendMessage(
111
        int $receiverUserId,
112
        string $subject,
113
        string $content,
114
        array $attachments = [],
115
        array $fileCommentList = [],
116
        int $groupId = 0,
117
        int $parentId = 0,
118
        int $editMessageId = 0,
119
        int $senderId = 0,
120
        int $forwardId = 0,
121
        bool $checkCurrentAudioId = false,
122
        bool $forceTitleWhenSendingEmail = false,
123
        ?int $msgType = null
124
    ): ?int {
125
        $sender = $this->userRepository->find($senderId);
126
        $receiver = $this->userRepository->find($receiverUserId);
127
128
        if (!$sender || !$receiver || !$receiver->isActive()) {
129
            return null;
130
        }
131
132
        $totalFileSize = 0;
133
        $attachmentList = $this->processAttachments($attachments, $fileCommentList, $totalFileSize);
134
135
        if ($totalFileSize > (int) $this->settingsManager->getSetting('message.message_max_upload_filesize')) {
136
            throw new Exception('Files size exceeds allowed limit.');
137
        }
138
139
        $parent = $this->messageRepository->find($parentId);
140
141
        if ($editMessageId) {
142
            $message = $this->messageRepository->find($editMessageId);
143
            if ($message) {
144
                $message->setTitle($subject);
145
                $message->setContent($content);
146
            }
147
        } else {
148
            $message = new Message();
149
            $message->setSender($sender)
150
                ->addReceiverTo($receiver)
151
                ->setTitle($subject)
152
                ->setContent($content)
153
                ->setGroup($groupId ? $this->getGroupById($groupId) : null)
154
                ->setParent($parent)
155
            ;
156
157
            if (null !== $msgType) {
158
                $message->setMsgType($msgType);
159
            }
160
        }
161
162
        $this->entityManager->persist($message);
163
        $this->entityManager->flush();
164
165
        if ($forwardId) {
166
            $this->forwardAttachments($forwardId, $message);
167
        }
168
169
        if ($checkCurrentAudioId) {
170
            $this->attachAudioMessage($message);
171
        }
172
173
        $this->saveAttachments($attachmentList, $message);
174
175
        $this->addSenderAsReceiver($message, $sender);
176
177
        if ($forceTitleWhenSendingEmail) {
178
            $this->sendEmailNotification($receiver, $sender, $subject, $content, $attachmentList);
179
        }
180
181
        return $message->getId();
182
    }
183
184
    /**
185
     * Processes attachments, calculates total file size, and returns the attachment list.
186
     *
187
     * @param mixed $totalFileSize
188
     */
189
    private function processAttachments(array $attachments, array $fileCommentList, &$totalFileSize): array
190
    {
191
        $attachmentList = [];
192
        foreach ($attachments as $index => $attachment) {
193
            $comment = $fileCommentList[$index] ?? '';
194
            $size = $attachment['size'] ?? 0;
195
196
            if (\is_array($size)) {
197
                foreach ($size as $s) {
198
                    $totalFileSize += $s;
199
                }
200
            } else {
201
                $totalFileSize += $size;
202
            }
203
204
            $attachmentList[] = [
205
                'file' => $attachment,
206
                'comment' => $comment,
207
            ];
208
        }
209
210
        return $attachmentList;
211
    }
212
213
    /**
214
     * Forwards attachments from one message to another.
215
     */
216
    private function forwardAttachments(int $forwardId, Message $message): void
217
    {
218
        $forwardMessage = $this->messageRepository->find($forwardId);
219
        if ($forwardMessage) {
220
            foreach ($forwardMessage->getAttachments() as $attachment) {
221
                $message->addAttachment($attachment);
222
            }
223
            $this->entityManager->persist($message);
224
            $this->entityManager->flush();
225
        }
226
    }
227
228
    /**
229
     * Attaches an audio message from the current session to the message.
230
     */
231
    private function attachAudioMessage(Message $message): void
232
    {
233
        if ($this->session && $this->session->has('current_audio')) {
234
            $audio = $this->session->get('current_audio');
235
236
            if (!empty($audio['name'])) {
237
                $attachment = new MessageAttachment();
238
                $attachment->setFilename($audio['name'])
239
                    ->setComment('audio_message')
240
                    ->setMessage($message)
241
                ;
242
243
                $message->addAttachment($attachment);
244
245
                $this->entityManager->persist($attachment);
246
                $this->entityManager->flush();
247
            }
248
        }
249
    }
250
251
    /**
252
     * Saves the provided attachments and links them to the message.
253
     */
254
    private function saveAttachments(array $attachments, Message $message): void
255
    {
256
        foreach ($attachments as $attachment) {
257
            $file = $attachment['file'];
258
            $comment = $attachment['comment'] ?? '';
259
260
            if ($file instanceof UploadedFile && UPLOAD_ERR_OK === $file->getError()) {
261
                $attachmentEntity = new MessageAttachment();
262
                $attachmentEntity->setFilename($file->getClientOriginalName())
263
                    ->setSize($file->getSize())
264
                    ->setPath($file->getRealPath())
265
                    ->setMessage($message)
266
                    ->setComment($comment)
267
                ;
268
269
                $message->addAttachment($attachmentEntity);
270
                $this->entityManager->persist($attachmentEntity);
271
            }
272
        }
273
        $this->entityManager->flush();
274
    }
275
276
    /**
277
     * Adds the sender as a receiver in the message to keep track of the sent message.
278
     */
279
    private function addSenderAsReceiver(Message $message, User $sender): void
280
    {
281
        $messageRelUserRepository = $this->entityManager->getRepository(MessageRelUser::class);
282
        $existingRelation = $messageRelUserRepository->findOneBy([
283
            'message' => $message,
284
            'receiver' => $sender,
285
            'receiverType' => MessageRelUser::TYPE_SENDER,
286
        ]);
287
288
        if (!$existingRelation) {
289
            $messageRelUserSender = new MessageRelUser();
290
            $messageRelUserSender->setMessage($message)
291
                ->setReceiver($sender)
292
                ->setReceiverType(MessageRelUser::TYPE_SENDER)
293
            ;
294
            $this->entityManager->persist($messageRelUserSender);
295
            $this->entityManager->flush();
296
        }
297
    }
298
299
    private function sendEmailNotification(User $receiver, User $sender, string $subject, string $content, array $attachmentList): void
300
    {
301
        $toAddress = $receiver->getEmail();
302
        if (!filter_var($toAddress, FILTER_VALIDATE_EMAIL)) {
303
            return;
304
        }
305
306
        $from = $this->buildFromAddress();
307
308
        try {
309
            $email = (new Email())
310
                ->from($from)
311
                ->to(new Address($toAddress, $receiver->getFullname() ?: $receiver->getUsername()))
312
                ->subject($subject)
313
                ->text($content)
314
                ->html($content);
315
316
            foreach ($attachmentList as $att) {
317
                $file = $att['file'] ?? null;
318
                if ($file instanceof UploadedFile && UPLOAD_ERR_OK === $file->getError()) {
319
                    $email->attachFromPath($file->getRealPath(), $file->getClientOriginalName());
320
                }
321
            }
322
323
            $this->mailer->send($email);
324
        } catch (\Throwable $e) {
325
            error_log('Failed to send email: ' . $e->getMessage());
326
        }
327
    }
328
    /**
329
     * Constructs the FROM as an Address, prioritizing configuration;
330
     * If there is no valid value, infers the current domain and uses noreply@{domain}.
331
     */
332
    private function buildFromAddress(): Address
333
    {
334
        $fromName = $this->settingsManager->getSetting('mail.mailer_from_name')
335
            ?: $this->settingsManager->getSetting('platform.site_name', true)
336
                ?: 'Chamilo';
337
338
        $candidates = [
339
            $this->settingsManager->getSetting('mail.mailer_from_email'),
340
            $this->settingsManager->getSetting('mail.mailer_from_address'),
341
            $this->settingsManager->getSetting('platform.administrator_email'),
342
        ];
343
        foreach ($candidates as $cand) {
344
            if ($cand && filter_var($cand, FILTER_VALIDATE_EMAIL)) {
345
                return new Address($cand, $fromName);
346
            }
347
        }
348
349
        $host = null;
350
        $accessUrl = $this->accessUrlHelper->getCurrent();
351
        if ($accessUrl && method_exists($accessUrl, 'getUrl')) {
352
            $host = parse_url((string) $accessUrl->getUrl(), PHP_URL_HOST);
353
        }
354
        if (!$host) {
355
            $req = $this->requestStack->getCurrentRequest();
356
            $host = $req?->getHost();
357
        }
358
359
        if (!$host || !str_contains($host, '.')) {
360
            $host = 'example.org';
361
        }
362
363
        return new Address('noreply@' . $host, $fromName);
364
    }
365
366
    /**
367
     * Retrieves a user group by its ID.
368
     */
369
    private function getGroupById(int $groupId)
370
    {
371
        return $this->entityManager->getRepository(Usergroup::class)->find($groupId);
372
    }
373
}
374