chamilo/chamilo-lms

View on GitHub
src/CoreBundle/ServiceHelper/MessageHelper.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

/* For licensing terms, see /license.txt */

declare(strict_types=1);

namespace Chamilo\CoreBundle\ServiceHelper;

use Chamilo\CoreBundle\Entity\Message;
use Chamilo\CoreBundle\Entity\MessageAttachment;
use Chamilo\CoreBundle\Entity\MessageRelUser;
use Chamilo\CoreBundle\Entity\User;
use Chamilo\CoreBundle\Entity\Usergroup;
use Chamilo\CoreBundle\Repository\MessageRepository;
use Chamilo\CoreBundle\Repository\Node\UserRepository;
use Chamilo\CoreBundle\Settings\SettingsManager;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

use const PHP_SAPI;
use const UPLOAD_ERR_OK;

class MessageHelper
{
    private ?SessionInterface $session = null;

    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly MessageRepository $messageRepository,
        private readonly UserRepository $userRepository,
        private readonly RequestStack $requestStack,
        private readonly AccessUrlHelper $accessUrlHelper,
        private readonly SettingsManager $settingsManager,
        private readonly MailerInterface $mailer
    ) {
        if (PHP_SAPI !== 'cli') {
            $this->session = $this->requestStack->getSession();
        }
    }

    /**
     * Sends a simple message with optional attachments and notifications to HR users.
     */
    public function sendMessageSimple(
        int $receiverUserId,
        string $subject,
        string $message,
        int $senderId = 0,
        bool $sendCopyToDrhUsers = false,
        bool $uploadFiles = true,
        array $attachmentList = []
    ): ?int {
        $files = $_FILES ?: [];
        if (false === $uploadFiles) {
            $files = [];
        }

        if (!empty($attachmentList)) {
            $files = $attachmentList;
        }

        $result = $this->sendMessage(
            $receiverUserId,
            $subject,
            $message,
            $files,
            [],
            0,
            0,
            0,
            $senderId
        );

        if ($sendCopyToDrhUsers) {
            $accessUrl = $this->accessUrlHelper->getCurrent();
            if (null !== $accessUrl) {
                $drhList = $this->userRepository->getDrhListFromUser($receiverUserId, $accessUrl->getId());
                if (!empty($drhList)) {
                    $receiverInfo = $this->userRepository->find($receiverUserId);

                    foreach ($drhList as $drhUser) {
                        $drhMessage = \sprintf(
                            'Copy of message sent to %s',
                            $receiverInfo->getFirstname().' '.$receiverInfo->getLastname()
                        ).' <br />'.$message;

                        $this->sendMessageSimple(
                            $drhUser->getId(),
                            $subject,
                            $drhMessage,
                            $senderId
                        );
                    }
                }
            }
        }

        return $result;
    }

    /**
     * Sends a message with attachments, forwards, and additional settings.
     */
    public function sendMessage(
        int $receiverUserId,
        string $subject,
        string $content,
        array $attachments = [],
        array $fileCommentList = [],
        int $groupId = 0,
        int $parentId = 0,
        int $editMessageId = 0,
        int $senderId = 0,
        int $forwardId = 0,
        bool $checkCurrentAudioId = false,
        bool $forceTitleWhenSendingEmail = false,
        ?int $msgType = null
    ): ?int {
        $sender = $this->userRepository->find($senderId);
        $receiver = $this->userRepository->find($receiverUserId);

        if (!$sender || !$receiver || !$receiver->isActive()) {
            return null;
        }

        $totalFileSize = 0;
        $attachmentList = $this->processAttachments($attachments, $fileCommentList, $totalFileSize);

        if ($totalFileSize > (int) $this->settingsManager->getSetting('message.message_max_upload_filesize')) {
            throw new Exception('Files size exceeds allowed limit.');
        }

        $parent = $this->messageRepository->find($parentId);

        if ($editMessageId) {
            $message = $this->messageRepository->find($editMessageId);
            if ($message) {
                $message->setTitle($subject);
                $message->setContent($content);
            }
        } else {
            $message = new Message();
            $message->setSender($sender)
                ->addReceiverTo($receiver)
                ->setTitle($subject)
                ->setContent($content)
                ->setGroup($groupId ? $this->getGroupById($groupId) : null)
                ->setParent($parent)
            ;

            if (null !== $msgType) {
                $message->setMsgType($msgType);
            }
        }

        $this->entityManager->persist($message);
        $this->entityManager->flush();

        if ($forwardId) {
            $this->forwardAttachments($forwardId, $message);
        }

        if ($checkCurrentAudioId) {
            $this->attachAudioMessage($message);
        }

        $this->saveAttachments($attachmentList, $message);

        $this->addSenderAsReceiver($message, $sender);

        if ($forceTitleWhenSendingEmail) {
            $this->sendEmailNotification($receiver, $sender, $subject, $content, $attachmentList);
        }

        return $message->getId();
    }

    /**
     * Processes attachments, calculates total file size, and returns the attachment list.
     *
     * @param mixed $totalFileSize
     */
    private function processAttachments(array $attachments, array $fileCommentList, &$totalFileSize): array
    {
        $attachmentList = [];
        foreach ($attachments as $index => $attachment) {
            $comment = $fileCommentList[$index] ?? '';
            $size = $attachment['size'] ?? 0;

            if (\is_array($size)) {
                foreach ($size as $s) {
                    $totalFileSize += $s;
                }
            } else {
                $totalFileSize += $size;
            }

            $attachmentList[] = [
                'file' => $attachment,
                'comment' => $comment,
            ];
        }

        return $attachmentList;
    }

    /**
     * Forwards attachments from one message to another.
     */
    private function forwardAttachments(int $forwardId, Message $message): void
    {
        $forwardMessage = $this->messageRepository->find($forwardId);
        if ($forwardMessage) {
            foreach ($forwardMessage->getAttachments() as $attachment) {
                $message->addAttachment($attachment);
            }
            $this->entityManager->persist($message);
            $this->entityManager->flush();
        }
    }

    /**
     * Attaches an audio message from the current session to the message.
     */
    private function attachAudioMessage(Message $message): void
    {
        if ($this->session && $this->session->has('current_audio')) {
            $audio = $this->session->get('current_audio');

            if (!empty($audio['name'])) {
                $attachment = new MessageAttachment();
                $attachment->setFilename($audio['name'])
                    ->setComment('audio_message')
                    ->setMessage($message)
                ;

                $message->addAttachment($attachment);

                $this->entityManager->persist($attachment);
                $this->entityManager->flush();
            }
        }
    }

    /**
     * Saves the provided attachments and links them to the message.
     */
    private function saveAttachments(array $attachments, Message $message): void
    {
        foreach ($attachments as $attachment) {
            $file = $attachment['file'];
            $comment = $attachment['comment'] ?? '';

            if ($file instanceof UploadedFile && UPLOAD_ERR_OK === $file->getError()) {
                $attachmentEntity = new MessageAttachment();
                $attachmentEntity->setFilename($file->getClientOriginalName())
                    ->setSize($file->getSize())
                    ->setPath($file->getRealPath())
                    ->setMessage($message)
                    ->setComment($comment)
                ;

                $message->addAttachment($attachmentEntity);
                $this->entityManager->persist($attachmentEntity);
            }
        }
        $this->entityManager->flush();
    }

    /**
     * Adds the sender as a receiver in the message to keep track of the sent message.
     */
    private function addSenderAsReceiver(Message $message, User $sender): void
    {
        $messageRelUserRepository = $this->entityManager->getRepository(MessageRelUser::class);
        $existingRelation = $messageRelUserRepository->findOneBy([
            'message' => $message,
            'receiver' => $sender,
            'receiverType' => MessageRelUser::TYPE_SENDER,
        ]);

        if (!$existingRelation) {
            $messageRelUserSender = new MessageRelUser();
            $messageRelUserSender->setMessage($message)
                ->setReceiver($sender)
                ->setReceiverType(MessageRelUser::TYPE_SENDER)
            ;
            $this->entityManager->persist($messageRelUserSender);
            $this->entityManager->flush();
        }
    }

    private function sendEmailNotification(User $receiver, User $sender, string $subject, string $content, array $attachmentList): void
    {
        if (empty($receiver->getEmail())) {
            throw new Exception('The receiver does not have a valid email address.');
        }

        $email = (new Email())
            ->from($sender->getEmail())
            ->to($receiver->getEmail())
            ->subject($subject)
            ->text($content)
            ->html($content)
        ;

        foreach ($attachmentList as $attachment) {
            if ($attachment instanceof UploadedFile) {
                $email->attachFromPath($attachment->getRealPath(), $attachment->getClientOriginalName());
            }
        }

        try {
            $this->mailer->send($email);
        } catch (Exception $e) {
            error_log('Failed to send email: '.$e->getMessage());
        }
    }

    /**
     * Retrieves a user group by its ID.
     */
    private function getGroupById(int $groupId)
    {
        return $this->entityManager->getRepository(Usergroup::class)->find($groupId);
    }
}