Passed
Push — feature/unit-tests ( 632e05...3f6f78 )
by Daniel
06:05
created

UserMailer::getUserUsername()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 7
ccs 3
cts 4
cp 0.75
rs 10
cc 2
nc 2
nop 1
crap 2.0625
1
<?php
2
3
/*
4
 * This file is part of the Silverback API Component Bundle Project
5
 *
6
 * (c) Daniel West <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Silverback\ApiComponentBundle\Mailer;
15
16
use Silverback\ApiComponentBundle\Entity\User\AbstractUser;
17
use Silverback\ApiComponentBundle\Exception\InvalidArgumentException;
18
use Silverback\ApiComponentBundle\Exception\MailerTransportException;
19
use Silverback\ApiComponentBundle\Url\RefererUrlHelper;
20
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
21
use Symfony\Component\HttpFoundation\RequestStack;
22
use Symfony\Component\Mailer\Envelope;
23
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
24
use Symfony\Component\Mailer\MailerInterface;
25
use Symfony\Component\Mime\Address;
26
use Symfony\Component\Mime\RawMessage;
27
28
/**
29
 * @author Daniel West <[email protected]>
30
 */
31
class UserMailer
32
{
33
    private MailerInterface $mailer;
34
    private RefererUrlHelper $refererUrlHelper;
35
    private RequestStack $requestStack;
36
    private string $websiteName;
37
    private string $defaultPasswordResetPath;
38
    private string $defaultChangeEmailVerifyPath;
39
    private bool $sendUserWelcomeEmailEnabled;
40
    private bool $sendUserEnabledEmailEnabled;
41
    private bool $sendUserUsernameChangedEmailEnabled;
42
    private bool $sendUserPasswordChangedEmailEnabled;
43
44 5
    public function __construct(
45
        MailerInterface $mailer,
46
        RefererUrlHelper $refererUrlHelper,
47
        RequestStack $requestStack,
48
        string $websiteName,
49
        string $defaultPasswordResetPath,
50
        string $defaultChangeEmailVerifyPath,
51
        bool $sendUserWelcomeEmailEnabled = true,
52
        bool $sendUserEnabledEmailEnabled = true,
53
        bool $sendUserUsernameChangedEmailEnabled = true,
54
        bool $sendUserPasswordChangedEmailEnabled = true
55
    ) {
56 5
        $this->mailer = $mailer;
57 5
        $this->refererUrlHelper = $refererUrlHelper;
58 5
        $this->requestStack = $requestStack;
59 5
        $this->defaultPasswordResetPath = $defaultPasswordResetPath;
60 5
        $this->defaultChangeEmailVerifyPath = $defaultChangeEmailVerifyPath;
61 5
        $this->websiteName = $websiteName;
62 5
        $this->sendUserWelcomeEmailEnabled = $sendUserWelcomeEmailEnabled;
63 5
        $this->sendUserEnabledEmailEnabled = $sendUserEnabledEmailEnabled;
64 5
        $this->sendUserUsernameChangedEmailEnabled = $sendUserUsernameChangedEmailEnabled;
65 5
        $this->sendUserPasswordChangedEmailEnabled = $sendUserPasswordChangedEmailEnabled;
66 5
    }
67
68
    public function sendPasswordResetEmail(AbstractUser $user): void
69
    {
70
        $userUsername = $this->getUserUsername($user);
71
        $token = $user->getNewPasswordConfirmationToken();
72
        if (!$token) {
73
            throw new InvalidArgumentException('A new password confirmation token must be set to send the `password reset` email');
74
        }
75
        $resetUrl = $this->pathToReferrerUrl(
76
            $token,
77
            $userUsername,
78
            'resetPath',
79
            $this->defaultPasswordResetPath
80
        );
81
        $email = $this->createEmailMessage(
82
            'Your password reset request',
83
            'user_forgot_password.html.twig',
84
            $user,
85
            [
86
                'reset_url' => $resetUrl,
87
            ]
88
        );
89
        $this->send($email);
90
    }
91
92
    public function sendChangeEmailConfirmationEmail(AbstractUser $user): void
93
    {
94
        $userUsername = $this->getUserUsername($user);
95
        $verifyUrl = $this->getEmailConfirmationUrl($user, $userUsername);
96
        $email = $this->createEmailMessage(
97
            'Your password reset request',
98
            'user_verify_email.html.twig',
99
            $user,
100
            [
101
                'verify_url' => $verifyUrl,
102
            ]
103
        );
104
        $this->send($email);
105
    }
106
107 5
    public function sendUserWelcomeEmail(AbstractUser $user): void
108
    {
109 5
        if (!$this->sendUserWelcomeEmailEnabled) {
110
            return;
111
        }
112 5
        $userUsername = $this->getUserUsername($user);
113
        try {
114 5
            $verifyUrl = $this->getEmailConfirmationUrl($user, $userUsername);
115 5
        } catch (InvalidArgumentException $exception) {
116
            // if we have not set the email verify token this will be thrown. this is an optional token though.
117 5
            $verifyUrl = null;
118
        }
119 5
        $email = $this->createEmailMessage(
120 5
            sprintf('Welcome to %s', $this->websiteName),
121 5
            'user_welcome.html.twig',
122
            $user,
123
            [
124 5
                'verify_url' => $verifyUrl,
125
            ]
126
        );
127 5
        $this->send($email);
128 5
    }
129
130
    public function sendUserEnabledEmail(AbstractUser $user): void
131
    {
132
        if (!$this->sendUserEnabledEmailEnabled) {
133
            return;
134
        }
135
        $email = $this->createEmailMessage(
136
            'Your account has been enabled',
137
            'user_enabled.html.twig',
138
            $user
139
        );
140
        $this->send($email);
141
    }
142
143
    public function sendUsernameChangedEmail(AbstractUser $user): void
144
    {
145
        if (!$this->sendUserUsernameChangedEmailEnabled) {
146
            return;
147
        }
148
        $email = $this->createEmailMessage(
149
            'Your username has been changed',
150
            'username_changed.html.twig',
151
            $user
152
        );
153
        $this->send($email);
154
    }
155
156
    public function sendPasswordChangedEmail(AbstractUser $user): void
157
    {
158
        if (!$this->sendUserPasswordChangedEmailEnabled) {
159
            return;
160
        }
161
        $email = $this->createEmailMessage(
162
            'Your password has been changed',
163
            'user_password_changed.html.twig',
164
            $user
165
        );
166
        $this->send($email);
167
    }
168
169 5
    private function createEmailMessage(string $subject, string $htmlTemplate, AbstractUser $user, array $context = [], ?string $toEmail = null)
170
    {
171
        $defaultContext = [
172 5
            'user' => $user,
173 5
            'username' => $this->getUserUsername($user),
174 5
            'website_name' => $this->websiteName,
175
        ];
176 5
        if (null === $toEmail) {
177 5
            $toEmail = $this->getUserEmail($user);
178
        }
179
180 5
        return (new TemplatedEmail())
181 5
            ->to(Address::fromString($toEmail))
182 5
            ->subject($subject)
183 5
            ->htmlTemplate('@SilverbackApiComponent/emails/' . $htmlTemplate)
184 5
            ->context(array_merge($defaultContext, $context));
185
    }
186
187 5
    private function send(RawMessage $message, Envelope $envelope = null): void
188
    {
189
        try {
190 5
            $this->mailer->send($message, $envelope);
191
        } catch (TransportExceptionInterface $exception) {
192
            $exception = new MailerTransportException($exception->getMessage());
193
            $exception->appendDebug($exception->getDebug());
194
            throw $exception;
195
        }
196 5
    }
197
198 5
    private function getUserEmail(AbstractUser $user): string
199
    {
200 5
        if (!($userEmail = $user->getEmailAddress())) {
201
            throw new InvalidArgumentException('The user must have an email address set to send them any email');
202
        }
203
204 5
        return $userEmail;
205
    }
206
207 5
    private function getUserUsername(AbstractUser $user): string
208
    {
209 5
        if (!($userUsername = $user->getUsername())) {
210
            throw new InvalidArgumentException('The user must have a username set to send them any email');
211
        }
212
213 5
        return $userUsername;
214
    }
215
216 5
    private function getEmailConfirmationUrl(AbstractUser $user, string $userUsername): string
217
    {
218 5
        $token = $user->getNewEmailVerificationToken();
219 5
        if (!$token) {
220 4
            throw new InvalidArgumentException('A new email confirmation token must be set to send the `email verification` email');
221
        }
222
223 1
        return $this->pathToReferrerUrl(
224 1
            $token,
225
            $userUsername,
226 1
            'verifyPath',
227 1
            $this->defaultChangeEmailVerifyPath
228
        );
229
    }
230
231 1
    private function pathToReferrerUrl(string $token, string $email, string $queryKey, string $defaultPath): string
232
    {
233 1
        $request = $this->requestStack->getMasterRequest();
234 1
        $path = $request ? $request->query->get($queryKey, $defaultPath) : $defaultPath;
235 1
        $path = str_replace(['{{ token }}', '{{ email }}'], [$token, $email], $path);
236
237 1
        return $this->refererUrlHelper->getAbsoluteUrl($path);
238
    }
239
}
240