diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/application/messages/de.msg | 68 | ||||
-rw-r--r-- | src/application/mystic/forum/orm/User.php | 2 | ||||
-rw-r--r-- | src/application/views/form_login.php | 2 | ||||
-rw-r--r-- | src/application/views/view_user.php | 10 | ||||
-rw-r--r-- | src/index.php | 175 |
5 files changed, 234 insertions, 23 deletions
diff --git a/src/application/messages/de.msg b/src/application/messages/de.msg index cac30e7..c2d8416 100644 --- a/src/application/messages/de.msg +++ b/src/application/messages/de.msg @@ -146,6 +146,9 @@ metadata({ : "Display name:" = "Anzeigename:" +: "Password:" += "Passwort:" + : "Choose password:" = "Passwort festlegen:" @@ -355,7 +358,7 @@ metadata({ = "Passwort wiederholen:" : "The password reset link is either invalid or it expired" -= "Der Link zum Password Zurücksetzen ist entweder ungültig oder abgelaufen" += "Der Link zum Zurücksetzen des Passwortes ist entweder ungültig oder abgelaufen" : "Password reset successfully!" = "Passwort erfolgreich zurückgesetzt!" @@ -368,7 +371,7 @@ metadata({ : "Hello, %user_display_name%!\n" "\n" - "a password reset has been requested successfully! Please click the link below to set a new password:\n" + "A password reset has been requested successfully! Please click the link below to set a new password:\n" "%reset_link%\n" "\n" "If this wasn't you, you can safely ignore this email. The link will only be valid for one hour.\n" @@ -377,7 +380,7 @@ metadata({ "%forum_copyright%" = "Hallo, %user_display_name%!\n" "\n" - "das Zurücksetzen Ihres Passwortes wurde erfolgreich angefragt. Bitte klicken Sie auf den untenstehenden Link, um Ihr Passwort zurückzusetzen:\n" + "Das Zurücksetzen Ihres Passwortes wurde erfolgreich angefragt. Bitte klicken Sie auf den untenstehenden Link, um Ihr Passwort zurückzusetzen:\n" "%reset_link%\n" "\n" "Falls Sie dies nicht waren, können Sie diese E-Mail ignorieren. Der Link ist nur für eine Stunde gültig.\n" @@ -393,3 +396,62 @@ metadata({ : "I know my password and I want to %link%log in%/link%!" = "Ich kenne mein Passwort und möchte mich %link%anmelden%/link%!" + +: "Hello, %user_display_name%!\n" + "\n" + "We are sending this email to let you know your passwort has been reset successfully!\n" + "\n" + "Kind regards,\n" + "%forum_copyright%" += "Hallo, %user_display_name%!\n" + "\n" + "Wir senden Ihnen diese E-Mail, um Ihnen mitzuteilen, dass Ihr Passwort erfolgreich geändert wurde!\n" + "\n" + "Mit freundlichen Grüßen,\n" + "%forum_copyright%" + +: "Please activate your account" += "Bitte aktivieren Sie Ihr Nutzerkonto" + +: "Email address changed" += "E-Mail-Adresse geändert" + +: "Hello, %user_display_name%!\n" + "\n" + "Your email address has been successfully changed from %old_email% to %new_email%!\n" + "\n" + "Kind regards,\n" + "%forum_copyright%" += "Hallo, %user_display_name%!\n" + "\n" + "Ihre E-Mail-Adresse wurde erfolgreich von %old_email% zu %new_email% geändert!\n" + "\n" + "Mit freundlichen Grüßen,\n" + "%forum_copyright%" + +: "Your email address has been changed successfully!\nPlease click %link%here%/link% to return to your profile!" += "Ihre E-Mail-Adresse wurde erfolgreich geändert!\nBitte klicken Sie %link%hier%/link%, um zu Ihrem Profil zurückzukehren!" + +: "Please verify your email first!" += "Bitte bestätigen Sie zuerst Ihre E-Mail-Adresse!" + +: "Please verify your email address" += "Bitte bestätigen Sie Ihre E-Mail-Adresse!" + +: "Hello, %user_display_name%!\n" + "\n" + "Please verify your new email address by clicking the link below:\n" + "%verify_link%\n" + "\n" + "Kind regards,\n" + "%forum_copyright%" += "Hallo, %user_display_name%!\n" + "\n" + "Bitte bestätigen Sie Ihre E-Mail-Adresse, indem Sie auf folgenden Link klicken:\n" + "%verify_link%\n" + "\n" + "Mit freundlichen Grüßen,\n" + "%forum_copyright%" + +: "Failed to send verification email" += "Konnte Bestätigungsmail nicht versenden" diff --git a/src/application/mystic/forum/orm/User.php b/src/application/mystic/forum/orm/User.php index 1bf02f1..868f385 100644 --- a/src/application/mystic/forum/orm/User.php +++ b/src/application/mystic/forum/orm/User.php @@ -17,6 +17,8 @@ class User extends Entity { #[Unique] public string $name; public string $displayName; #[Unique] public string $email; + #[Unique] public ?string $pendingEmail; + public ?\DateTimeImmutable $pendingEmailCreated; public \DateTimeImmutable $created; public string $passwordHash; public int $permissionMask; diff --git a/src/application/views/form_login.php b/src/application/views/form_login.php index acef1ff..87ee516 100644 --- a/src/application/views/form_login.php +++ b/src/application/views/form_login.php @@ -35,7 +35,7 @@ if (($_formError = RequestUtils::getAndClearFormError("login")) !== null) { <div class="form-group"> <button class="btn btn-primary" type="submit"><?= __("Log in") ?></button> - <a href="?_action=pwreset"><?= __("I forgot my password") ?></a> + <a class="btn btn-link" href="?_action=pwreset"><?= __("I forgot my password") ?></a> </div> <div class="form-group"> diff --git a/src/application/views/view_user.php b/src/application/views/view_user.php index aba0f2f..d3ebe04 100644 --- a/src/application/views/view_user.php +++ b/src/application/views/view_user.php @@ -20,6 +20,8 @@ else $dateJoined = DateTime::createFromImmutable($user->created); $dateJoined->setTime(0, 0, 0, 0); + +$emailPending = $isOwnProfile && $user->pendingEmail !== null; ?> <div class="clearfix page-header margin-top-0"> @@ -93,7 +95,7 @@ if (($_formError = RequestUtils::getAndClearFormError("update_profile")) !== nul <div class="form-group"> <label for="i_name"><?= __("Username:") ?></label> <?php if ($lastNameChangeTooRecent): ?> - <input required class="form-control" type="text" id="i_name" value="<?= htmlentities($user->name) ?>" disabled> + <input class="form-control" type="text" id="i_name" value="<?= htmlentities($user->name) ?>" disabled> <small class="text-danger"><strong><?= __("You can only change your username every 30 days!") ?></strong></small> <?php else: ?> <input required class="form-control" type="text" name="name" id="i_name" value="<?= htmlentities($user->name) ?>"> @@ -101,7 +103,11 @@ if (($_formError = RequestUtils::getAndClearFormError("update_profile")) !== nul </div> <div class="form-group"> <label for="i_email"><?= __("Email address:") ?></label> - <input required class="form-control" type="email" id="i_email" value="<?= htmlentities($user->email) ?>" disabled> + <?php if ($emailPending): ?> + <input class="form-control" type="email" id="i_email" value="<?= htmlentities($user->email) ?>" disabled> + <?php else: ?> + <input required class="form-control" type="email" id="i_email" name="email" value="<?= htmlentities($user->email) ?>"> + <?php endif; ?> </div> <div class="form-group"> <label><?= __("Profile picture:") ?></label> diff --git a/src/index.php b/src/index.php index 62874e6..d651400 100644 --- a/src/index.php +++ b/src/index.php @@ -441,10 +441,9 @@ if ($_action === "auth") { $sig = $_GET["sig"] ?? throw new Exception("Missing signature"); $user = new User(); - $user->activated = false; $user->activationToken = $token; - if (!$db->fetchWhere($user, [ "activated", "activation_token" ])) { + if (!$db->fetchWhere($user, "activation_token")) { http_response_code(400); msg_error(__("Invalid token")); exit; @@ -458,22 +457,101 @@ if ($_action === "auth") { exit; } - $user->activated = true; - $user->activationToken = ""; + $isActivation = !$user->activated; + if ($isActivation) { + $user->activated = true; + $user->activationToken = ""; - if (!$db->update($user)) { - http_response_code(400); - msg_error(__("Failed to update user")); - exit; - } + if (!$db->update($user)) { + http_response_code(400); + msg_error(__("Failed to update user")); + exit; + } - msg_info("?!HTML::" . __( - "Your account has been activated!\nPlease click %link%here%/link% to log in!", - [ - "link" => '<a href="?_action=auth">', - "/link" => '</a>', - ] - )); + msg_info("?!HTML::" . __( + "Your account has been activated!\nPlease click %link%here%/link% to log in!", + [ + "link" => '<a href="?_action=auth">', + "/link" => '</a>', + ] + )); + } else { + $oldEmail = $user->email; + $newEmail = $user->pendingEmail; + + $user->activationToken = ""; + $user->email = $user->pendingEmail; + $user->pendingEmail = null; + $user->pendingEmailCreated = null; + + if (!$db->update($user)) { + http_response_code(400); + msg_error(__("Failed to update user")); + exit; + } + + $transport = Transport::fromDsn(env("MAILER_DSN")); + + try { + $transport->send( + (new Email()) + ->from(env("MAILER_FROM")) + ->to(new Address($oldEmail, $user->displayName)) + ->text(__( + "Hello, %user_display_name%!\n" . + "\n" . + "Your email address has been successfully changed from %old_email% to %new_email%!\n" . + "\n" . + "Kind regards,\n" . + "%forum_copyright%", + params: [ + "forum_title" => (env("MYSTIC_FORUM_TITLE") ?? "Forum"), + "user_display_name" => $user->displayName, + "old_email" => $oldEmail, + "new_email" => $newEmail, + "forum_copyright" => (env("MYSTIC_FORUM_COPYRIGHT") ?? env("MYSTIC_FORUM_TITLE") ?? "Forum") + ] + )) + ->subject(__("Email address changed")) + ); + } catch (TransportException $_) { + // fail silently + } + + try { + $transport->send( + (new Email()) + ->from(env("MAILER_FROM")) + ->to(new Address($newEmail, $user->displayName)) + ->text(__( + "Hello, %user_display_name%!\n" . + "\n" . + "Your email address has been successfully changed from %old_email% to %new_email%!\n" . + "\n" . + "Kind regards,\n" . + "%forum_copyright%", + params: [ + "forum_title" => (env("MYSTIC_FORUM_TITLE") ?? "Forum"), + "user_display_name" => $user->displayName, + "old_email" => $oldEmail, + "new_email" => $newEmail, + "forum_copyright" => (env("MYSTIC_FORUM_COPYRIGHT") ?? env("MYSTIC_FORUM_TITLE") ?? "Forum") + ] + )) + ->subject(__("Email address changed")) + ); + } catch (TransportException $_) { + // fail silently + } + + msg_info("?!HTML::" . __( + "Your email address has been changed successfully!\nPlease click %link%here%/link% to return to your profile!", + [ + "link" => '<a href="?_action=viewuser&user=' . htmlentities(urlencode($user->id)) . '">', + "/link" => '</a>', + ] + )); + } } elseif ($_action === "logout") { RequestUtils::unsetAuthorizedUser(); header("Location: " . ($_GET["next"] ?? ".")); @@ -766,6 +844,7 @@ if ($_action === "auth") { $pfpAction = RequestUtils::getRequiredField("pfp_action", $formId); $userName = $_POST["name"] ?? $user->name; + $email = $_POST["email"] ?? $user->email; $user->displayName = $displayName; @@ -784,6 +863,48 @@ if ($_action === "auth") { } } + if ($email !== $user->email) { + if ($user->pendingEmailCreated !== null) { + RequestUtils::triggerFormError(__("Please verify your email first!"), $formId); + } else { + $queryUser = new User(); + $queryUser->email = $email; + $queryUser->pendingEmail = $email; + if ($db->fetchWhere($queryUser, "email") || $db->fetchWhere($queryUser, "pending_email")) { + RequestUtils::triggerFormError(__("This email address is already in use!"), $formId); + } + $user->pendingEmail = $email; + $user->pendingEmailCreated = new DateTimeImmutable(); + $user->activationToken = $db->generateId(12); + + try { + Transport::fromDsn(env("MAILER_DSN"))->send( + (new Email()) + ->from(env("MAILER_FROM")) + ->to(new Address($email, $displayName)) + ->text(__( + "Hello, %user_display_name%!\n" . + "\n" . + "Please verify your new email address by clicking the link below:\n" . + "%verify_link%\n" . + "\n" . + "Kind regards,\n" . + "%forum_copyright%", + params: [ + "forum_title" => (env("MYSTIC_FORUM_TITLE") ?? "Forum"), + "user_display_name" => $displayName, + "verify_link" => env("PUBLIC_URL") . "?_action=verifyemail&token=" . urlencode($user->activationToken) . "&sig=" . urlencode(base64_encode(hash("sha256", env("SECRET") . $user->activationToken . $user->id, true))), + "forum_copyright" => (env("MYSTIC_FORUM_COPYRIGHT") ?? env("MYSTIC_FORUM_TITLE") ?? "Forum") + ] + )) + ->subject(__("Please verify your email address")) + ); + } catch (TransportException $_) { + RequestUtils::triggerFormError(__("Failed to send verification email"), $formId); + } + } + } + switch ($pfpAction) { case "keep": // Do nothing @@ -1386,6 +1507,26 @@ if ($_action === "auth") { RequestUtils::triggerFormError(__("Failed to update password"), $formId); } + Transport::fromDsn(env("MAILER_DSN"))->send( + (new Email()) + ->from(env("MAILER_FROM")) + ->to(new Address($resetUser->email, $resetUser->displayName)) + ->text(__( + "Hello, %user_display_name%!\n" . + "\n" . + "We are sending this email to let you know your passwort has been reset successfully!\n" . + "\n" . + "Kind regards,\n" . + "%forum_copyright%", + params: [ + "forum_title" => (env("MYSTIC_FORUM_TITLE") ?? "Forum"), + "user_display_name" => $resetUser->displayName, + "forum_copyright" => (env("MYSTIC_FORUM_COPYRIGHT") ?? env("MYSTIC_FORUM_TITLE") ?? "Forum") + ] + )) + ->subject(__("Password reset successfully!")) + ); + msg_info(__("Password reset successfully!"), true); } else { $formId = "pwreset"; @@ -1403,7 +1544,7 @@ if ($_action === "auth") { ->text(__( "Hello, %user_display_name%!\n" . "\n" . - "a password reset has been requested successfully! Please click the link below to set a new password:\n" . + "A password reset has been requested successfully! Please click the link below to set a new password:\n" . "%reset_link%\n" . "\n" . "If this wasn't you, you can safely ignore this email. The link will only be valid for one hour.\n" . |