Skip to content

Commit f3e01fa

Browse files
committed
Fix CMS-2016 - Triggering activation email sends email verification link
This splits up the activation email and email verification email into two separate concepts. I mistook them for the same flow when porting this functionality earlier.
1 parent b7dae18 commit f3e01fa

13 files changed

Lines changed: 151 additions & 22 deletions

File tree

src/Http/Controllers/Users/ActivateController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public function sendActivationEmail(Request $request): Response
112112
$userVariable = $request->getSigned('userVariable') ?? 'user';
113113

114114
try {
115-
$user->sendEmailVerificationNotification();
115+
$this->users->sendActivationEmail($user);
116116
} catch (Throwable $e) {
117117
return $this->asModelFailure(
118118
$user,

src/Http/Controllers/Users/PermissionsController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public function store(Request $request, Elements $elements): Response
103103
$request->boolean('sendActivationEmail')
104104
) {
105105
try {
106-
$user->sendEmailVerificationNotification();
106+
Users::sendActivationEmail($user);
107107
} catch (Throwable $e) {
108108
Flash::error(t('Couldn’t send the activation email: {error}', [
109109
'error' => $e->getMessage(),

src/Http/Controllers/Users/SaveUserController.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,9 @@ public function __invoke(Request $request): Response
317317
$originalEmail = $user->email;
318318
$user->email = $user->unverifiedEmail;
319319

320-
$user->sendEmailVerificationNotification();
320+
$isNewUser
321+
? $this->users->sendActivationEmail($user)
322+
: $this->users->sendNewEmailVerifyEmail($user);
321323

322324
// Put the original email back into place
323325
$user->email = $originalEmail;

src/Support/Facades/Users.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
* @method static void saveUserPreferences(\CraftCms\Cms\User\Elements\User $user, array $preferences)
1717
* @method static mixed getUserPreference(int $userId, string $key, mixed $default = null)
1818
* @method static bool sendPasswordResetEmail(\CraftCms\Cms\User\Elements\User $user)
19-
* @method static string getActivationUrl(\CraftCms\Cms\User\Elements\User $user)
19+
* @method static bool sendActivationEmail(\CraftCms\Cms\User\Elements\User $user)
20+
* @method static bool sendNewEmailVerifyEmail(\CraftCms\Cms\User\Elements\User $user)
21+
* @method static string getActivationUrl(\CraftCms\Cms\User\Elements\User $user, string|null $token = null)
2022
* @method static string getEmailVerifyUrl(\CraftCms\Cms\User\Elements\User $user, string|null $token = null)
2123
* @method static string getPasswordResetUrl(\CraftCms\Cms\User\Elements\User $user, string|null $token = null)
2224
* @method static void removeCredentials(\CraftCms\Cms\User\Elements\User $user)

src/User/Commands/CreateCommand.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ public function handle(Elements $elements, GeneralConfig $generalConfig, Users $
152152
if ($this->option('send-activation-email') || confirm('Send an activation email now?')) {
153153
$this->components->task(
154154
'Sending activation email...',
155-
function () use ($user) {
156-
$user->sendEmailVerificationNotification();
155+
function () use ($users, $user) {
156+
$users->sendActivationEmail($user);
157157
}
158158
);
159159

src/User/Elements/User.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
use CraftCms\Cms\User\Events\DefineFriendlyName;
5858
use CraftCms\Cms\User\Events\DefineName;
5959
use CraftCms\Cms\User\Models\User as UserModel;
60+
use CraftCms\Cms\User\Notifications\ActivationNotification;
6061
use CraftCms\Cms\User\Notifications\ResetPasswordNotification;
6162
use CraftCms\Cms\User\Notifications\VerifyEmailNotification;
6263
use CraftCms\Cms\User\Validation\UserRules;
@@ -734,6 +735,11 @@ public function sendPasswordResetNotification($token): void
734735
$this->notify(new ResetPasswordNotification($token));
735736
}
736737

738+
public function sendActivationNotification(string $token): void
739+
{
740+
$this->notify(new ActivationNotification($token));
741+
}
742+
737743
public function hasVerifiedEmail(): bool
738744
{
739745
return is_null($this->unverifiedEmail);
@@ -2038,7 +2044,9 @@ public function afterSave(bool $isNew): void
20382044
$originalEmail = $this->email;
20392045
$this->email = $this->unverifiedEmail;
20402046

2041-
$this->sendEmailVerificationNotification();
2047+
$isNew
2048+
? Users::sendActivationEmail($this)
2049+
: Users::sendNewEmailVerifyEmail($this);
20422050

20432051
// Put the original email back into place
20442052
$this->email = $originalEmail;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CraftCms\Cms\User\Notifications;
6+
7+
use CraftCms\Cms\Cms;
8+
use CraftCms\Cms\Support\Facades\Users;
9+
use CraftCms\Cms\Support\Template;
10+
use CraftCms\Cms\SystemMessage\Mailables\SystemMessageMailable;
11+
use CraftCms\Cms\SystemMessage\SystemMessages;
12+
use CraftCms\Cms\User\Elements\User;
13+
use Illuminate\Bus\Queueable;
14+
use Illuminate\Contracts\Queue\ShouldQueue;
15+
use Illuminate\Notifications\Channels\MailChannel;
16+
use Illuminate\Notifications\Notification;
17+
use SensitiveParameter;
18+
19+
class ActivationNotification extends Notification implements ShouldQueue
20+
{
21+
use Queueable;
22+
23+
public function __construct(
24+
#[SensitiveParameter]
25+
public string $token,
26+
) {
27+
$this->queue = Cms::config()->queueName;
28+
}
29+
30+
public function via(mixed $notifiable): array
31+
{
32+
return [MailChannel::class];
33+
}
34+
35+
public function toMail(User $user): SystemMessageMailable
36+
{
37+
$url = Users::getActivationUrl($user, $this->token);
38+
39+
return app(SystemMessages::class)->mailable(
40+
key: 'account_activation',
41+
user: $user,
42+
variables: ['link' => Template::raw($url)],
43+
);
44+
}
45+
}

src/User/Notifications/ResetPasswordNotification.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function via(mixed $notifiable): array
3232

3333
public function toMail(User $user): SystemMessageMailable
3434
{
35-
$url = Users::getPasswordResetUrl($user);
35+
$url = Users::getPasswordResetUrl($user, $this->token);
3636

3737
return app(SystemMessages::class)->mailable(
3838
key: 'forgot_password',

src/User/Notifications/VerifyEmailNotification.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@ public function via(mixed $notifiable): array
3535
public function toMail(User $user): SystemMessageMailable
3636
{
3737
$url = Users::getEmailVerifyUrl($user, $this->token);
38-
$key = $user->pending ? 'account_activation' : 'verify_new_email';
3938

4039
return app(SystemMessages::class)->mailable(
41-
key: $key,
40+
key: 'verify_new_email',
4241
user: $user,
4342
variables: ['link' => Template::raw($url)],
4443
);

src/User/Users.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,20 +252,44 @@ public function sendPasswordResetEmail(User $user): bool
252252
return Password::broker('craft')->sendResetLink(['loginName' => $user->email]) === Password::RESET_LINK_SENT;
253253
}
254254

255+
/**
256+
* Sends a new account activation email to a user.
257+
*
258+
* @throws InvalidElementException if the user doesn't validate
259+
*/
260+
public function sendActivationEmail(User $user): bool
261+
{
262+
$user->sendActivationNotification($this->setVerificationCodeOnUser($user));
263+
264+
return true;
265+
}
266+
267+
/**
268+
* Sends a new email verification email to a user.
269+
*
270+
* @throws InvalidElementException if the user doesn't validate
271+
*/
272+
public function sendNewEmailVerifyEmail(User $user): bool
273+
{
274+
$user->sendEmailVerificationNotification();
275+
276+
return true;
277+
}
278+
255279
/**
256280
* Sets a new verification code on a user, and returns their activation URL.
257281
*
258282
*
259283
* @throws InvalidElementException if the user doesn't validate
260284
*/
261-
public function getActivationUrl(User $user): string
285+
public function getActivationUrl(User $user, ?string $token = null): string
262286
{
263287
// If the user doesn't have a password yet, use a Password Reset URL
264-
if (! $user->password) {
265-
return $this->getPasswordResetUrl($user);
288+
if (! $user->getHasPassword()) {
289+
return $this->getPasswordResetUrl($user, $token);
266290
}
267291

268-
return $this->getEmailVerifyUrl($user);
292+
return $this->getEmailVerifyUrl($user, $token);
269293
}
270294

271295
/**

0 commit comments

Comments
 (0)