Util/Mails

* make sendMail a member of Util
 * move mail templates from strings to template files
 * enabled debug output to page
This commit is contained in:
Sarjuuk
2025-07-27 18:05:48 +02:00
parent 40b5c992e2
commit ceec228718
27 changed files with 236 additions and 63 deletions

View File

@@ -1228,6 +1228,56 @@ abstract class Util
return array_values($menu);
}
public static function sendMail(string $email, string $tplFile, array $vars = [], int $expiration = 0) : bool
{
if (!self::isValidEmail($email))
return false;
$template = '';
if (file_exists('template/mails/'.$tplFile.'_'.User::$preferedLoc->value.'.tpl'))
$template = file_get_contents('template/mails/'.$tplFile.'_'.User::$preferedLoc->value.'.tpl');
else
{
foreach (Locale::cases() as $l)
{
if (!$l->validate() || !file_exists('template/mails/'.$tplFile.'_'.$l->value.'.tpl'))
continue;
$template = file_get_contents('template/mails/'.$tplFile.'_'.$l->value.'.tpl');
break;
}
}
if (!$template)
{
trigger_error('Util::SendMail() - mail template not found: '.$tplFile, E_USER_ERROR);
return false;
}
[, $subject, $body] = explode("\n", $template, 3);
$body = Util::defStatic($body);
if ($vars)
$body = vsprintf($body, $vars);
if ($expiration)
$body .= "\n\n".Lang::account('tokenExpires', [Util::formatTime($expiration * 1000)])."\n";
$subject = Cfg::get('NAME_SHORT').Lang::main('colon') . $subject;
$header = 'From: ' . Cfg::get('CONTACT_EMAIL') . "\n" .
'Reply-To: ' . Cfg::get('CONTACT_EMAIL') . "\n" .
'X-Mailer: PHP/' . phpversion();
if (Cfg::get('DEBUG') >= LOG_LEVEL_INFO && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN))
{
$_SESSION['debug-mail'] = $email . "\n\n" . $subject . "\n\n" . $body;
return true;
}
return mail($email, $subject, $body, $header);
}
}
?>

View File

@@ -898,6 +898,7 @@ $lang = array(
'recoverUser' => "Benutzernamenanfrage",
'recoverPass' => "Kennwort zurücksetzen: Schritt %s von 2",
'newPass' => "Neues Kennwort",
'tokenExpires' => "Das Token wird in %s verfallen.",
// creation
'register' => "Registrierung: Schritt %s von 2",
@@ -954,12 +955,7 @@ $lang = array(
'comments' => "Kommentare",
'screenshots' => "Screenshots",
'videos' => "Videos",
'posts' => "Forenbeiträge",
// user mail
'tokenExpires' => "Das Token wird in %s verfallen.",
'accConfirm' => ["Kontobestätigung", "Willkommen bei CFG_NAME_SHORT!\r\n\r\nKlicke auf den Link um euren Account zu aktivieren.\r\n\r\nHOST_URL?account=signup&token=%s\r\n\r\nFalls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden."],
'recoverUser' => ["Benutzernamenanfrage", "Folgt diesem Link um euch anzumelden.\r\n\r\nHOST_URL?account=signin&token=%s\r\n\r\nFalls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden."],
'resetPass' => ["Kennwortreset", "Folgt diesem Link um euer Kennwort zurückzusetzen.\r\n\r\nHOST_URL?account=forgotpassword&token=%s\r\n\r\nFalls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden."]
'posts' => "Forenbeiträge"
),
'emote' => array(
'notFound' => "Dieses Emote existiert nicht.",

View File

@@ -898,6 +898,7 @@ $lang = array(
'recoverUser' => "Username Request",
'recoverPass' => "Password Reset: Step %s of 2",
'newPass' => "New Password",
'tokenExpires' => "This token expires in %s.",
// creation
'register' => "Registration - Step %s of 2",
@@ -954,12 +955,7 @@ $lang = array(
'comments' => "Comments",
'screenshots' => "Screenshots",
'videos' => "Videos",
'posts' => "Forum posts",
// user mail
'tokenExpires' => "This token expires in %s.",
'accConfirm' => ["Account Confirmation", "Welcome to CFG_NAME_SHORT!\r\n\r\nClick the Link below to activate your account.\r\n\r\nHOST_URL?account=signup&token=%s\r\n\r\nIf you did not request this mail simply ignore it."],
'recoverUser' => ["User Recovery", "Follow this link to log in.\r\n\r\nHOST_URL?account=signin&token=%s\r\n\r\nIf you did not request this mail simply ignore it."],
'resetPass' => ["Password Reset", "Follow this link to reset your password.\r\n\r\nHOST_URL?account=forgotpassword&token=%s\r\n\r\nIf you did not request this mail simply ignore it."]
'posts' => "Forum posts"
),
'emote' => array(
'notFound' => "This Emote doesn't exist.",

View File

@@ -898,6 +898,7 @@ $lang = array(
'recoverUser' => "Pedir nombre de usuario",
'recoverPass' => "Reiniciar contraseña: Paso %s de 2",
'newPass' => "Nueva Contraseña",
'tokenExpires' => "Este token expira en %s",
// creation
'register' => "Inscripción: Paso %s de 2",
@@ -954,12 +955,7 @@ $lang = array(
'comments' => "Comentarios",
'screenshots' => "Capturas de pantalla",
'videos' => "Vídeos",
'posts' => "Mensajes en los foros",
// user mail
'tokenExpires' => "Este token expira en %s",
'accConfirm' => ["Confirmación de Cuenta", "Bienvenido a CFG_NAME_SHORT!\r\n\r\nHaga click en el enlace siguiente para activar su cuenta.\r\n\r\nHOST_URL?account=signup&token=%s\r\n\r\nSi usted no solicitó este correo, por favor ignorelo."],
'recoverUser' => ["Recuperacion de Usuario", "Siga a este enlace para ingresar.\r\n\r\nHOST_URL?account=signin&token=%s\r\n\r\nSi usted no solicitó este correo, por favor ignorelo."],
'resetPass' => ["Reinicio de Contraseña", "Siga este enlace para reiniciar su contraseña.\r\n\r\nHOST_URL?account=forgotpassword&token=%s\r\n\r\nSi usted no solicitó este correo, por favor ignorelo."]
'posts' => "Mensajes en los foros"
),
'emote' => array(
'notFound' => "Este emoticón no existe.",

View File

@@ -898,6 +898,7 @@ $lang = array(
'recoverUser' => "Demande de nom d'utilisateur",
'recoverPass' => "Changement de mot de passe : Étape %s de 2",
'newPass' => "Nouveau mot de passe",
'tokenExpires' => "This token expires in %s.",
// creation
'register' => "Enregistrement : Étape %s de 2",
@@ -954,12 +955,7 @@ $lang = array(
'comments' => "Commentaires",
'screenshots' => "Captures d'écran",
'videos' => "Vidéos",
'posts' => "Messages sur le forum",
// user mail
'tokenExpires' => "This token expires in %s.",
'accConfirm' => ["Activation de compte", "Bienvenue sur CFG_NAME_SHORT!\r\n\r\nCliquez sur le lien ci-dessous pour activer votre compte.\r\n\r\nHOST_URL?account=signup&token=%s\r\n\r\nSi vous n'avez pas demandé cet email, ignorez le."],
'recoverUser' => ["Récupération d'utilisateur", "Suivez ce lien pour vous connecter.\r\n\r\nHOST_URL?account=signin&token=%s\r\n\r\nSi vous n'avez pas demandé cet email, ignorez le."],
'resetPass' => ["Réinitialisation du mot de passe", "Suivez ce lien pour réinitialiser votre mot de passe.\r\n\r\nHOST_URL?account=forgotpassword&token=%s\r\n\r\nSi vous n'avez pas fait de demande de réinitialisation, ignorez cet email."]
'posts' => "Messages sur le forum"
),
'emote' => array(
'notFound' => "[This Emote doesn't exist.]",

View File

@@ -898,6 +898,7 @@ $lang = array(
'recoverUser' => "Запрос имени пользователя",
'recoverPass' => "Сброс пароля: Шаг %s из 2",
'newPass' => "New Password",
'tokenExpires' => "This token expires in %s.",
// creation
'register' => "Регистрация: Шаг %s из 2",
@@ -954,12 +955,7 @@ $lang = array(
'comments' => "Комментарии",
'screenshots' => "Скриншоты",
'videos' => "Видео",
'posts' => "Сообщений на форумах",
// user mail
'tokenExpires' => "This token expires in %s.",
'accConfirm' => ["Account Confirmation", "Welcome to CFG_NAME_SHORT!\r\n\r\nClick the Link below to activate your account.\r\n\r\nHOST_URL?account=signup&token=%s\r\n\r\nIf you did not request this mail simply ignore it."],
'recoverUser' => ["User Recovery", "Follow this link to log in.\r\n\r\nHOST_URL?account=signin&token=%s\r\n\r\nIf you did not request this mail simply ignore it."],
'resetPass' => ["Password Reset", "Follow this link to reset your password.\r\n\r\nHOST_URL?account=forgotpassword&token=%s\r\n\r\nIf you did not request this mail simply ignore it."]
'posts' => "Сообщений на форумах"
),
'emote' => array(
'notFound' => "[This Emote doesn't exist.]",

View File

@@ -898,6 +898,7 @@ $lang = array(
'recoverUser' => "用户名需求",
'recoverPass' => "密码重置:步骤 %s / 2",
'newPass' => "新密码",
'tokenExpires' => "此令牌将在%s过期。",
// creation
'register' => "注册 - 步骤 %s / 2",
@@ -954,12 +955,7 @@ $lang = array(
'comments' => "评论",
'screenshots' => "截图",
'videos' => "视频",
'posts' => "论坛帖子",
// user mail
'tokenExpires' => "此令牌将在%s过期。",
'accConfirm' => ["账户确认", "欢迎来到CFG_NAME_SHORT!\r\n\r\n点击下方链接以激活您的账户。\r\n\r\nHOST_URL?account=signup&token=%s\r\n\r\n如果您没有请求此邮件,请忽略它。"],
'recoverUser' => ["用户恢复", "点击此链接登录\r\n\r\nHOST_URL?account=signin&token=%s\r\n\r\n如果您没有请求此邮件,请忽略它。"],
'resetPass' => ["重置密码", "点击此链接以重置您的密码。\r\n\r\nHOST_URL?account=forgotpassword&token=%s\r\n\r\n如果您没有请求此邮件,请忽略它。"]
'posts' => "论坛帖子"
),
'emote' => array(
'notFound' => "这个表情不存在。",

View File

@@ -450,8 +450,10 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
);
if (!$ok)
return Lang::main('intError');
else if ($_ = $this->sendMail(Lang::user('accConfirm', 0), sprintf(Lang::user('accConfirm', 1), $token), Cfg::get('ACC_CREATE_SAVE_DECAY')))
{
if (!Util::sendMail($this->_post['email'], 'activate-account', [$token], Cfg::get('ACC_RECOVERY_DECAY')))
return Lang::main('intError2', ['send mail']);
if ($id = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE token = ?', $token))
Util::gainSiteReputation($id, SITEREP_ACTION_REGISTER);
@@ -460,9 +462,6 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 1, 1, UNIX_TIMESTAMP() + ?d)', User::$ip, Cfg::get('ACC_FAILED_AUTH_BLOCK'));
else
DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ? AND type = 1', Cfg::get('ACC_FAILED_AUTH_BLOCK'), User::$ip);
return $_;
}
}
private function doRecoverPass()
@@ -471,7 +470,8 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
return $_;
// send recovery mail
return $this->sendMail(Lang::user('resetPass', 0), sprintf(Lang::user('resetPass', 1), $token), Cfg::get('ACC_RECOVERY_DECAY'));
if (!Util::sendMail($this->_post['email'], 'reset-password', [$token], Cfg::get('ACC_RECOVERY_DECAY')))
return Lang::main('intError2', ['send mail']);
}
private function doResetPass()
@@ -502,8 +502,8 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
if ($_ = $this->initRecovery(ACC_STATUS_RECOVER_USER, Cfg::get('ACC_RECOVERY_DECAY'), $token))
return $_;
// send recovery mail
return $this->sendMail(Lang::user('recoverUser', 0), sprintf(Lang::user('recoverUser', 1), $token), Cfg::get('ACC_RECOVERY_DECAY'));
if (!Util::sendMail($this->_post['email'], 'recover-user', [$token], Cfg::get('ACC_RECOVERY_DECAY')))
return Lang::main('intError2', ['send mail']);
}
private function initRecovery($type, $delay, &$token)
@@ -521,19 +521,6 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
return Lang::main('intError');
}
private function sendMail($subj, $msg, $delay = 300)
{
// send recovery mail
$subj = Cfg::get('NAME_SHORT').Lang::main('colon') . $subj;
$msg .= "\r\n\r\n".sprintf(Lang::user('tokenExpires'), Util::formatTime($delay * 1000))."\r\n";
$header = 'From: '.Cfg::get('CONTACT_EMAIL') . "\r\n" .
'Reply-To: '.Cfg::get('CONTACT_EMAIL') . "\r\n" .
'X-Mailer: PHP/' . phpversion();
if (!mail($this->_post['email'], $subj, $msg, $header))
return sprintf(Lang::main('intError2'), 'send mail');
}
private function getNext($forHeader = false)
{
$next = $forHeader ? '.' : '';

View File

@@ -623,6 +623,22 @@ class GenericPage
);
}
// debug output from Util::sendMail
if (isset($_SESSION['debug-mail']))
{
if (Cfg::get('DEBUG') >= LOG_LEVEL_INFO && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN))
$announcements[65535] = array(
'parent' => 'announcement-65535',
'id' => 0,
'mode' => 0,
'status' => 1,
'name' => 'Debug sendmail',
'style' => 'color: #'.$colors[LOG_LEVEL_INFO].'; font-weight: bold; font-size: 14px; padding-left: 40px; background-image: url('.Cfg::get('STATIC_URL').'/images/announcements/warn-small.png); background-size: 15px 15px; background-position: 12px center; border: dashed 2px #'.$colors[LOG_LEVEL_INFO].';',
'text' => '[span]'.$_SESSION['debug-mail'].'[/span]'
);
unset($_SESSION['debug-mail']);
}
// fetch announcements
if ($this->pageTemplate['pageName'])
{

View File

@@ -0,0 +1,10 @@
# Created: May 2018
Account Creation
Hey!
Thanks a lot for your interest in contributing to the site.
Just click this link to activate your account:
HOST_URL?account=signup&token=%s
The NAME_SHORT team

View File

@@ -0,0 +1,10 @@
# Created: May 2018
Account Creation
Bonjour !
Nous vous remercions chaleureusement pour l'intérêt que vous portez à contribuer à notre site.
Vous n'avez qu'à cliquer sur le lien ci-dessous pour activer votre compte.
HOST_URL?account=signup&token=%s
L'équipe NAME_SHORT

View File

@@ -0,0 +1,10 @@
# Created: May 2018
Kontoerstellung
Hey!
Vielen Dank für Euer Interesse an der Mitwirkung bei unserer Webseite.
Klickt einfach auf den folgenden Link, um Euer Konto zu aktivieren:
HOST_URL?account=signup&token=%s
Das Team von NAME_SHORT

View File

@@ -0,0 +1,10 @@
# Created by ChatGPT from May 2018 base; locale 0
账户创建
你好!
非常感谢你有兴趣为本站做出贡献。
只需点击此链接激活你的账户:
HOST_URL?account=signup&token=%s
NAME_SHORT 团队敬上

View File

@@ -0,0 +1,10 @@
# Created by ChatGPT from May 2018 base; locale 0
Creación de cuenta
¡Hola!
Muchas gracias por tu interés en contribuir al sitio.
Simplemente haz clic en este enlace para activar tu cuenta:
HOST_URL?account=signup&token=%s
El equipo de NAME_SHORT

View File

@@ -0,0 +1,10 @@
# Created: May 2018
Account Creation
Привет!
Благодарим за ваш интерес по наполнению сайта.
Для активации вашей учетной записи просто кликните по этой ссылке:
HOST_URL?account=signup&token=%s
Команда NAME_SHORT.

View File

@@ -0,0 +1,7 @@
# Legacy import
User Recovery
Follow this link to log in.
HOST_URL?account=signin&token=%s
If you did not request this mail simply ignore it.

View File

@@ -0,0 +1,7 @@
# Legacy import
Récupération d'utilisateur
Suivez ce lien pour vous connecter.
HOST_URL?account=signin&token=%s
Si vous n'avez pas demandé cet e-mail, ignorez le.

View File

@@ -0,0 +1,7 @@
# Legacy import
Benutzernamenanfrage
Folgt diesem Link um euch anzumelden.
HOST_URL?account=signin&token=%s
Falls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden.

View File

@@ -0,0 +1,7 @@
# Legacy import
用户恢复
请点击此链接登录。
HOST_URL?account=signin&token=%s
如果您没有请求此邮件,请忽略它。

View File

@@ -0,0 +1,7 @@
# Legacy import
Recuperacion de Usuario
Siga a este enlace para ingresar.
HOST_URL?account=signin&token=%s
Si usted no solicitó este correo, por favor ignorelo.

View File

@@ -0,0 +1,7 @@
# Created by ChatGPT from legacy import; locale 0
Восстановление пользователя
Перейдите по этой ссылке, чтобы войти в систему.
HOST_URL?account=signin&token=%s
Если вы не запрашивали это письмо, просто проигнорируйте его.

View File

@@ -0,0 +1,7 @@
# Legacy import
Password Reset
Follow this link to reset your password.
HOST_URL?account=forgotpassword&token=%s
If you did not request this mail simply ignore it.

View File

@@ -0,0 +1,7 @@
# Legacy import
Réinitialisation du mot de passe
Suivez ce lien pour réinitialiser votre mot de passe.
HOST_URL?account=forgotpassword&token=%s
Si vous n'avez pas fait de demande de réinitialisation, ignorez cet e-mail.

View File

@@ -0,0 +1,7 @@
# Legacy import
Kennwortreset
Folgt diesem Link um euer Kennwort zurückzusetzen.
HOST_URL?account=forgotpassword&token=%s
Falls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden.

View File

@@ -0,0 +1,7 @@
# Legacy import
重置密码
点击此链接以重置您的密码。
HOST_URL?account=forgotpassword&token=%s
如果您没有请求此邮件,请忽略它。

View File

@@ -0,0 +1,7 @@
# Legacy import
Reinicio de Contraseña
Siga este enlace para reiniciar su contraseña.
HOST_URL?account=forgotpassword&token=%s
Si usted no solicitó este correo, por favor ignorelo.

View File

@@ -0,0 +1,7 @@
# Created by ChatGPT from legacy import; locale 0
Сброс пароля
Перейдите по этой ссылке, чтобы сбросить свой пароль.
HOST_URL?account=forgotpassword&token=%s
Если вы не запрашивали это письмо, просто проигнорируйте его.