Passed
Push — dependabot/npm_and_yarn/microm... ( f2f212...f35bf2 )
by
unknown
13:36 queued 05:58
created

sendReminder()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 36
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 26
c 1
b 0
f 0
nc 4
nop 3
dl 0
loc 36
rs 9.504
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Command;
8
9
use DateInterval;
10
use DateTime;
11
use DateTimeZone;
12
use Doctrine\DBAL\Connection;
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use Symfony\Component\Console\Style\SymfonyStyle;
18
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
19
use Symfony\Component\Mailer\MailerInterface;
20
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
21
use Chamilo\CoreBundle\Settings\SettingsManager;
22
23
class SendCourseExpirationRemindersCommand extends Command
24
{
25
    protected static $defaultName = 'app:send-course-expiration-reminders';
26
27
    public function __construct(
28
        private readonly Connection $connection,
29
        private readonly MailerInterface $mailer,
30
        private readonly SettingsManager $settingsManager
31
    ) {
32
        parent::__construct();
33
    }
34
35
    protected function configure(): void
36
    {
37
        $this
38
            ->setDescription('Send course expiration reminders to users.')
39
            ->addOption('debug', null, InputOption::VALUE_NONE, 'Enable debug mode')
40
            ->setHelp('This command sends email reminders to users before their course access expires.');
41
    }
42
43
    protected function execute(InputInterface $input, OutputInterface $output): int
44
    {
45
        $io = new SymfonyStyle($input, $output);
46
        $debug = $input->getOption('debug');
47
48
        if ($debug) {
49
            $io->note('Debug mode activated');
50
        }
51
52
        $isActive = 'true' === $this->settingsManager->getSetting('crons.cron_remind_course_expiration_activate');
53
        if (!$isActive) {
54
            $io->warning('Course expiration reminder cron is not active.');
55
            return Command::SUCCESS;
56
        }
57
58
        $frequency = (int) $this->settingsManager->getSetting('crons.cron_remind_course_expiration_frequency');
59
        $today = new DateTime('now', new DateTimeZone('UTC'));
60
        $expirationDate = (clone $today)->add(new DateInterval("P{$frequency}D"))->format('Y-m-d');
61
62
        $sessions = $this->getSessionsExpiringBetween($today->format('Y-m-d'), $expirationDate);
63
64
        if (empty($sessions)) {
65
            $io->success("No users to be reminded.");
66
            return Command::SUCCESS;
67
        }
68
69
        foreach ($sessions as $session) {
70
            $this->sendReminder($session, $io, $debug);
71
        }
72
73
        $io->success('Course expiration reminders sent successfully.');
74
        return Command::SUCCESS;
75
    }
76
77
    private function getSessionsExpiringBetween(string $today, string $expirationDate): array
78
    {
79
        $sql = "
80
        SELECT DISTINCT category.session_id, certificate.user_id, session.access_end_date, session.title as name
81
        FROM gradebook_category AS category
82
        LEFT JOIN gradebook_certificate AS certificate ON category.id = certificate.cat_id
83
        INNER JOIN session AS session ON category.session_id = session.id
84
        WHERE session.access_end_date BETWEEN :today AND :expirationDate
85
        AND category.session_id IS NOT NULL AND certificate.user_id IS NOT NULL
86
    ";
87
88
        return $this->connection->fetchAllAssociative($sql, [
89
            'today' => $today,
90
            'expirationDate' => $expirationDate
91
        ]);
92
    }
93
94
95
    private function sendReminder(array $session, SymfonyStyle $io, bool $debug): void
96
    {
97
        $userInfo = $this->getUserInfo((int) $session['user_id']);
98
        $userInfo['complete_name'] = $userInfo['firstname'] . ' ' . $userInfo['lastname'];
99
        $remainingDays = $this->calculateRemainingDays($session['access_end_date']);
100
101
        $administrator = [
102
            'completeName' => $this->settingsManager->getSetting('admin.administrator_name'),
103
            'email' => $this->settingsManager->getSetting('admin.administrator_email'),
104
        ];
105
106
        $institution = $this->settingsManager->getSetting('platform.institution');
107
        $rootWeb = $this->settingsManager->getSetting('platform.institution_url');
108
109
        $email = (new TemplatedEmail())
110
            ->from($administrator['email'])
111
            ->to($userInfo['email'])
112
            ->subject('Course Expiration Reminder')
113
            ->htmlTemplate('@ChamiloCore/Mailer/Legacy/cron_remind_course_expiration_body.html.twig')
114
            ->context([
115
                'complete_user_name' => $userInfo['complete_name'],
116
                'session_name' => $session['name'],
117
                'session_access_end_date' => $session['access_end_date'],
118
                'remaining_days' => $remainingDays,
119
                'institution' => $institution,
120
                'root_web' => $rootWeb,
121
            ]);
122
123
        try {
124
            $this->mailer->send($email);
125
126
            if ($debug) {
127
                $io->note("Reminder sent to {$userInfo['complete_name']} ({$userInfo['email']}) for session: {$session['name']}");
128
            }
129
        } catch (TransportExceptionInterface $e) {
130
            $io->error("Failed to send reminder: {$e->getMessage()}");
131
        }
132
    }
133
134
    private function getUserInfo(int $userId): array
135
    {
136
        $sql = "SELECT * FROM user WHERE id = :userId";
137
        return $this->connection->fetchAssociative($sql, ['userId' => $userId]);
138
    }
139
140
    private function calculateRemainingDays(string $accessEndDate): string
141
    {
142
        $today = new DateTime('now', new DateTimeZone('UTC'));
143
        $endDate = new DateTime($accessEndDate);
144
        return $today->diff($endDate)->format('%d');
145
    }
146
}
147