Passed
Pull Request — master (#6396)
by Angel Fernando Quiroz
08:47
created

MessageHelper::sendEmailNotification()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 15
nc 7
nop 5
dl 0
loc 24
rs 9.4555
c 0
b 0
f 0
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\Email;
24
25
use const PHP_SAPI;
26
use const UPLOAD_ERR_OK;
27
28
class MessageHelper
29
{
30
    private ?SessionInterface $session = null;
31
32
    public function __construct(
33
        private readonly EntityManagerInterface $entityManager,
34
        private readonly MessageRepository $messageRepository,
35
        private readonly UserRepository $userRepository,
36
        private readonly RequestStack $requestStack,
37
        private readonly AccessUrlHelper $accessUrlHelper,
38
        private readonly SettingsManager $settingsManager,
39
        private readonly MailerInterface $mailer
40
    ) {
41
        if (PHP_SAPI !== 'cli') {
42
            $this->session = $this->requestStack->getSession();
43
        }
44
    }
45
46
    /**
47
     * Sends a simple message with optional attachments and notifications to HR users.
48
     */
49
    public function sendMessageSimple(
50
        int $receiverUserId,
51
        string $subject,
52
        string $message,
53
        int $senderId = 0,
54
        bool $sendCopyToDrhUsers = false,
55
        bool $uploadFiles = true,
56
        array $attachmentList = []
57
    ): ?int {
58
        $files = $_FILES ?: [];
59
        if (false === $uploadFiles) {
60
            $files = [];
61
        }
62
63
        if (!empty($attachmentList)) {
64
            $files = $attachmentList;
65
        }
66
67
        $result = $this->sendMessage(
68
            $receiverUserId,
69
            $subject,
70
            $message,
71
            $files,
72
            [],
73
            0,
74
            0,
75
            0,
76
            $senderId
77
        );
78
79
        if ($sendCopyToDrhUsers) {
80
            $accessUrl = $this->accessUrlHelper->getCurrent();
81
            if (null !== $accessUrl) {
82
                $drhList = $this->userRepository->getDrhListFromUser($receiverUserId, $accessUrl->getId());
83
                if (!empty($drhList)) {
84
                    $receiverInfo = $this->userRepository->find($receiverUserId);
85
86
                    foreach ($drhList as $drhUser) {
87
                        $drhMessage = \sprintf(
88
                            'Copy of message sent to %s',
89
                            $receiverInfo->getFirstname().' '.$receiverInfo->getLastname()
90
                        ).' <br />'.$message;
91
92
                        $this->sendMessageSimple(
93
                            $drhUser->getId(),
94
                            $subject,
95
                            $drhMessage,
96
                            $senderId
97
                        );
98
                    }
99
                }
100
            }
101
        }
102
103
        return $result;
104
    }
105
106
    /**
107
     * Sends a message with attachments, forwards, and additional settings.
108
     */
109
    public function sendMessage(
110
        int $receiverUserId,
111
        string $subject,
112
        string $content,
113
        array $attachments = [],
114
        array $fileCommentList = [],
115
        int $groupId = 0,
116
        int $parentId = 0,
117
        int $editMessageId = 0,
118
        int $senderId = 0,
119
        int $forwardId = 0,
120
        bool $checkCurrentAudioId = false,
121
        bool $forceTitleWhenSendingEmail = false,
122
        ?int $msgType = null
123
    ): ?int {
124
        $sender = $this->userRepository->find($senderId);
125
        $receiver = $this->userRepository->find($receiverUserId);
126
127
        if (!$sender || !$receiver || !$receiver->isActive()) {
128
            return null;
129
        }
130
131
        $totalFileSize = 0;
132
        $attachmentList = $this->processAttachments($attachments, $fileCommentList, $totalFileSize);
133
134
        if ($totalFileSize > (int) $this->settingsManager->getSetting('message.message_max_upload_filesize')) {
135
            throw new Exception('Files size exceeds allowed limit.');
136
        }
137
138
        $parent = $this->messageRepository->find($parentId);
139
140
        if ($editMessageId) {
141
            $message = $this->messageRepository->find($editMessageId);
142
            if ($message) {
143
                $message->setTitle($subject);
144
                $message->setContent($content);
145
            }
146
        } else {
147
            $message = new Message();
148
            $message->setSender($sender)
149
                ->addReceiverTo($receiver)
150
                ->setTitle($subject)
151
                ->setContent($content)
152
                ->setGroup($groupId ? $this->getGroupById($groupId) : null)
153
                ->setParent($parent)
154
            ;
155
156
            if (null !== $msgType) {
157
                $message->setMsgType($msgType);
158
            }
159
        }
160
161
        $this->entityManager->persist($message);
162
        $this->entityManager->flush();
163
164
        if ($forwardId) {
165
            $this->forwardAttachments($forwardId, $message);
166
        }
167
168
        if ($checkCurrentAudioId) {
169
            $this->attachAudioMessage($message);
170
        }
171
172
        $this->saveAttachments($attachmentList, $message);
173
174
        $this->addSenderAsReceiver($message, $sender);
175
176
        if ($forceTitleWhenSendingEmail) {
177
            $this->sendEmailNotification($receiver, $sender, $subject, $content, $attachmentList);
178
        }
179
180
        return $message->getId();
181
    }
182
183
    /**
184
     * Processes attachments, calculates total file size, and returns the attachment list.
185
     *
186
     * @param mixed $totalFileSize
187
     */
188
    private function processAttachments(array $attachments, array $fileCommentList, &$totalFileSize): array
189
    {
190
        $attachmentList = [];
191
        foreach ($attachments as $index => $attachment) {
192
            $comment = $fileCommentList[$index] ?? '';
193
            $size = $attachment['size'] ?? 0;
194
195
            if (\is_array($size)) {
196
                foreach ($size as $s) {
197
                    $totalFileSize += $s;
198
                }
199
            } else {
200
                $totalFileSize += $size;
201
            }
202
203
            $attachmentList[] = [
204
                'file' => $attachment,
205
                'comment' => $comment,
206
            ];
207
        }
208
209
        return $attachmentList;
210
    }
211
212
    /**
213
     * Forwards attachments from one message to another.
214
     */
215
    private function forwardAttachments(int $forwardId, Message $message): void
216
    {
217
        $forwardMessage = $this->messageRepository->find($forwardId);
218
        if ($forwardMessage) {
219
            foreach ($forwardMessage->getAttachments() as $attachment) {
220
                $message->addAttachment($attachment);
221
            }
222
            $this->entityManager->persist($message);
223
            $this->entityManager->flush();
224
        }
225
    }
226
227
    /**
228
     * Attaches an audio message from the current session to the message.
229
     */
230
    private function attachAudioMessage(Message $message): void
231
    {
232
        if ($this->session && $this->session->has('current_audio')) {
233
            $audio = $this->session->get('current_audio');
234
235
            if (!empty($audio['name'])) {
236
                $attachment = new MessageAttachment();
237
                $attachment->setFilename($audio['name'])
238
                    ->setComment('audio_message')
239
                    ->setMessage($message)
240
                ;
241
242
                $message->addAttachment($attachment);
243
244
                $this->entityManager->persist($attachment);
245
                $this->entityManager->flush();
246
            }
247
        }
248
    }
249
250
    /**
251
     * Saves the provided attachments and links them to the message.
252
     */
253
    private function saveAttachments(array $attachments, Message $message): void
254
    {
255
        foreach ($attachments as $attachment) {
256
            $file = $attachment['file'];
257
            $comment = $attachment['comment'] ?? '';
258
259
            if ($file instanceof UploadedFile && UPLOAD_ERR_OK === $file->getError()) {
260
                $attachmentEntity = new MessageAttachment();
261
                $attachmentEntity->setFilename($file->getClientOriginalName())
262
                    ->setSize($file->getSize())
263
                    ->setPath($file->getRealPath())
264
                    ->setMessage($message)
265
                    ->setComment($comment)
266
                ;
267
268
                $message->addAttachment($attachmentEntity);
269
                $this->entityManager->persist($attachmentEntity);
270
            }
271
        }
272
        $this->entityManager->flush();
273
    }
274
275
    /**
276
     * Adds the sender as a receiver in the message to keep track of the sent message.
277
     */
278
    private function addSenderAsReceiver(Message $message, User $sender): void
279
    {
280
        $messageRelUserRepository = $this->entityManager->getRepository(MessageRelUser::class);
281
        $existingRelation = $messageRelUserRepository->findOneBy([
282
            'message' => $message,
283
            'receiver' => $sender,
284
            'receiverType' => MessageRelUser::TYPE_SENDER,
285
        ]);
286
287
        if (!$existingRelation) {
288
            $messageRelUserSender = new MessageRelUser();
289
            $messageRelUserSender->setMessage($message)
290
                ->setReceiver($sender)
291
                ->setReceiverType(MessageRelUser::TYPE_SENDER)
292
            ;
293
            $this->entityManager->persist($messageRelUserSender);
294
            $this->entityManager->flush();
295
        }
296
    }
297
298
    private function sendEmailNotification(User $receiver, User $sender, string $subject, string $content, array $attachmentList): void
299
    {
300
        if (empty($receiver->getEmail())) {
301
            throw new Exception('The receiver does not have a valid email address.');
302
        }
303
304
        $email = (new Email())
305
            ->from($sender->getEmail())
306
            ->to($receiver->getEmail())
307
            ->subject($subject)
308
            ->text($content)
309
            ->html($content)
310
        ;
311
312
        foreach ($attachmentList as $attachment) {
313
            if ($attachment instanceof UploadedFile) {
314
                $email->attachFromPath($attachment->getRealPath(), $attachment->getClientOriginalName());
315
            }
316
        }
317
318
        try {
319
            $this->mailer->send($email);
320
        } catch (Exception $e) {
321
            error_log('Failed to send email: '.$e->getMessage());
322
        }
323
    }
324
325
    /**
326
     * Retrieves a user group by its ID.
327
     */
328
    private function getGroupById(int $groupId)
329
    {
330
        return $this->entityManager->getRepository(Usergroup::class)->find($groupId);
331
    }
332
}
333