summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Kohl <git@jonaskohl.de>2024-09-19 13:37:59 +0200
committerJonas Kohl <git@jonaskohl.de>2024-09-19 13:37:59 +0200
commit06510101ad051c5b7bbacd6411eeb2b5c53cc3a3 (patch)
tree0678aea9b166c2e73f14c91f9246ac0b82298098
parentea64d16f7cdb86be610d5c90c46d0185d0e839e1 (diff)
Email editing
-rw-r--r--src/application/messages/de.msg68
-rw-r--r--src/application/mystic/forum/orm/User.php2
-rw-r--r--src/application/views/form_login.php2
-rw-r--r--src/application/views/view_user.php10
-rw-r--r--src/index.php175
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" .