Failed Conditions
Push — master ( b089ad...aace03 )
by Adrien
07:24
created

Mailer::modelMessageToMailMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 1
dl 0
loc 14
rs 9.9666
c 0
b 0
f 0
ccs 10
cts 10
cp 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Service;
6
7
use Application\DBAL\Types\MessageTypeType;
8
use Application\Model\Message;
9
use Application\Model\Transaction;
10
use Application\Model\User;
11
use Cake\Chronos\Chronos;
12
use Doctrine\ORM\EntityManager;
13
use Zend\Mail;
14
use Zend\Mail\Transport\TransportInterface;
15
use Zend\Mime\Message as MimeMessage;
16
use Zend\Mime\Mime;
17
use Zend\Mime\Part as MimePart;
18
use Zend\View\Model\ViewModel;
19
use Zend\View\Renderer\RendererInterface;
20
21
class Mailer
22
{
23
    /**
24
     * @var EntityManager
25
     */
26
    private $entityManager;
27
28
    /**
29
     * @var TransportInterface
30
     */
31
    private $transport;
32
33
    /**
34
     * @var string
35
     */
36
    private $hostname;
37
38
    /**
39
     * @var null|string
40
     */
41
    private $emailOverride;
42
43
    /**
44
     * @var RendererInterface
45
     */
46
    private $viewRenderer;
47
48
    /**
49
     * @var string
50
     */
51
    private $fromEmail;
52
53
    /**
54
     * @var string
55
     */
56
    private $phpPath;
57
58 7
    public function __construct(EntityManager $entityManager, TransportInterface $transport, RendererInterface $viewRenderer, string $hostname, ?string $emailOverride, string $fromEmail, string $phpPath)
59
    {
60 7
        $this->entityManager = $entityManager;
61 7
        $this->transport = $transport;
62 7
        $this->hostname = $hostname;
63 7
        $this->emailOverride = $emailOverride;
64 7
        $this->viewRenderer = $viewRenderer;
65 7
        $this->fromEmail = $fromEmail;
66 7
        $this->phpPath = $phpPath;
67 7
    }
68
69 2
    public function queueRegister(User $user): Message
70
    {
71 2
        $subject = 'Demande de création de compte au Club Nautique Ichtus';
72
        $mailParams = [
73 2
            'token' => $user->createToken(),
74
        ];
75
76 2
        $message = $this->createMessage($user, $user->getEmail(), $subject, MessageTypeType::REGISTER, $mailParams);
0 ignored issues
show
Bug introduced by
It seems like $user->getEmail() can also be of type null; however, parameter $email of Application\Service\Mailer::createMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

76
        $message = $this->createMessage($user, /** @scrutinizer ignore-type */ $user->getEmail(), $subject, MessageTypeType::REGISTER, $mailParams);
Loading history...
77
78 2
        return $message;
79
    }
80
81 2
    public function queueUnregister(User $admin, User $unregisteredUser): Message
82
    {
83 2
        $subject = 'Démission';
84
        $mailParams = [
85 2
            'unregisteredUser' => $unregisteredUser,
86
        ];
87
88 2
        $message = $this->createMessage($admin, $admin->getEmail(), $subject, MessageTypeType::UNREGISTER, $mailParams);
0 ignored issues
show
Bug introduced by
It seems like $admin->getEmail() can also be of type null; however, parameter $email of Application\Service\Mailer::createMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

88
        $message = $this->createMessage($admin, /** @scrutinizer ignore-type */ $admin->getEmail(), $subject, MessageTypeType::UNREGISTER, $mailParams);
Loading history...
89
90 2
        return $message;
91
    }
92
93
    /**
94
     * Queue a reset password email to specified user
95
     *
96
     * @param User $user The user for which a password reset will be done
97
     * @param string $email the address to send the email to. Might be different than the user's email
98
     *
99
     * @return Message
100
     */
101 3
    public function queueResetPassword(User $user, string $email): Message
102
    {
103 3
        $subject = 'Demande de modification de mot de passe';
104
        $mailParams = [
105 3
            'token' => $user->createToken(),
106
        ];
107
108 3
        $message = $this->createMessage($user, $email, $subject, MessageTypeType::RESET_PASSWORD, $mailParams);
109
110 3
        return $message;
111
    }
112
113 2
    public function queueInvoice(User $user, Transaction $transaction): Message
114
    {
115 2
        $total = '0';
116 2
        foreach ($transaction->getTransactionLines() as $line) {
117 2
            $total = bcadd($total, $line->getBalance(), 2);
118
        }
119 2
        $subject = 'Débit de compte';
120
        $mailParams = [
121 2
            'transaction' => $transaction,
122 2
            'total' => $total,
123
        ];
124
125 2
        $message = $this->createMessage($user, $user->getEmail(), $subject, MessageTypeType::INVOICE, $mailParams);
0 ignored issues
show
Bug introduced by
It seems like $user->getEmail() can also be of type null; however, parameter $email of Application\Service\Mailer::createMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

125
        $message = $this->createMessage($user, /** @scrutinizer ignore-type */ $user->getEmail(), $subject, MessageTypeType::INVOICE, $mailParams);
Loading history...
126
127 2
        return $message;
128
    }
129
130
    /**
131
     * Create a message by rendering the template
132
     *
133
     * @param null|User $user
134
     * @param string $email
135
     * @param string $subject
136
     * @param string $type
137
     * @param array $mailParams
138
     *
139
     * @return Message
140
     */
141 9
    private function createMessage(?User $user, string $email, string $subject, string $type, array $mailParams): Message
142
    {
143
144
        // First render the view
145 9
        $serverUrl = 'https://' . $this->hostname;
146 9
        $model = new ViewModel($mailParams);
147 9
        $model->setTemplate(str_replace('_', '-', $type));
148 9
        $model->setVariable('email', $email);
149 9
        $model->setVariable('user', $user);
150 9
        $model->setVariable('serverUrl', $serverUrl);
151 9
        $partialContent = $this->viewRenderer->render($model);
152
153
        // Then inject it into layout
154 9
        $layoutModel = new ViewModel([$model->captureTo() => $partialContent]);
155 9
        $layoutModel->setTemplate('layout');
156 9
        $layoutModel->setVariable('subject', $subject);
157 9
        $layoutModel->setVariable('user', $user);
158 9
        $layoutModel->setVariable('serverUrl', $serverUrl);
159 9
        $layoutModel->setVariable('hostname', $this->hostname);
160 9
        $content = $this->viewRenderer->render($layoutModel);
161
162 9
        $message = new Message();
163 9
        $message->setType($type);
164 9
        $message->setRecipient($user);
165 9
        $message->setSubject($subject);
166 9
        $message->setBody($content);
167 9
        $message->setEmail($email);
168 9
        $this->entityManager->persist($message);
169
170 9
        return $message;
171
    }
172
173
    /**
174
     * Send a message asynchronously in a separate process.
175
     *
176
     * This should be the preferred way to send a message, unless if we are the cron.
177
     *
178
     * @param Message $message
179
     */
180 4
    public function sendMessageAsync(Message $message): void
181
    {
182
        // Be sure we have an ID before "forking" process
183 4
        if ($message->getId() === null) {
0 ignored issues
show
introduced by
The condition $message->getId() === null is always false.
Loading history...
184 4
            _em()->flush();
185
        }
186
187
        $args = [
188 4
            realpath('bin/send-message.php'),
189 4
            $message->getId(),
190
        ];
191
192 4
        $escapedArgs = array_map('escapeshellarg', $args);
193
194 4
        $cmd = escapeshellcmd($this->phpPath) . ' ' . implode(' ', $escapedArgs) . ' > /dev/null 2>&1 &';
195 4
        exec($cmd);
196 4
    }
197
198
    /**
199
     * Send a message
200
     *
201
     * @param Message $message
202
     */
203 1
    public function sendMessage(Message $message): void
204
    {
205 1
        $mailMessage = $this->modelMessageToMailMessage($message);
206
207 1
        $email = $message->getEmail();
208 1
        $overriddenBy = '';
209 1
        if ($this->emailOverride) {
210
            $email = $this->emailOverride;
211
            $overriddenBy = ' overridden by ' . $email;
212
        }
213
214 1
        $recipientName = $message->getRecipient() ? $message->getRecipient()->getName() : null;
215 1
        if ($email) {
216 1
            $mailMessage->addTo($email, $recipientName);
217 1
            $this->transport->send($mailMessage);
218
        }
219
220 1
        $message->setDateSent(new Chronos());
221 1
        $this->entityManager->flush();
222
223 1
        echo 'email sent to: ' . $message->getEmail() . "\t" . $overriddenBy . "\t" . $message->getSubject() . PHP_EOL;
224 1
    }
225
226
    /**
227
     * Convert our model message to a mail message
228
     *
229
     * @param Message $modelMessage
230
     *
231
     * @return Mail\Message
232
     */
233 1
    private function modelMessageToMailMessage(Message $modelMessage): Mail\Message
234
    {
235
        // set Mime type html
236 1
        $htmlPart = new MimePart($modelMessage->getBody());
237 1
        $htmlPart->type = Mime::TYPE_HTML;
238 1
        $body = new MimeMessage();
239 1
        $body->setParts([$htmlPart]);
240
241 1
        $mailMessage = new Mail\Message();
242 1
        $mailMessage->setSubject($modelMessage->getSubject());
243 1
        $mailMessage->setBody($body);
244 1
        $mailMessage->setFrom($this->fromEmail, 'Ichtus');
245
246 1
        return $mailMessage;
247
    }
248
249
    /**
250
     * Send all messages that are not sent yet
251
     */
252
    public function sendAllMessages(): void
253
    {
254
        $messages = $this->entityManager->getRepository(Message::class)->getAllMessageToSend();
255
        foreach ($messages as $message) {
256
            $this->sendMessage($message);
257
        }
258
    }
259
}
260