mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-04-13 07:58:13 +02:00
Merge branch 'master' into develop
This commit is contained in:
102
CHANGELOG.md
102
CHANGELOG.md
@@ -1,3 +1,105 @@
|
||||
#### v2.5.7 (2022-10-14)
|
||||
|
||||
##### Chores
|
||||
|
||||
* incrementing version number - v2.5.6 (c7bd7dbf)
|
||||
* update changelog for v2.5.6 (e92238d0)
|
||||
* incrementing version number - v2.5.5 (3509ed94)
|
||||
* incrementing version number - v2.5.4 (e83260ca)
|
||||
* incrementing version number - v2.5.3 (7e922936)
|
||||
* incrementing version number - v2.5.2 (babcd17e)
|
||||
* incrementing version number - v2.5.1 (ce3aa950)
|
||||
* incrementing version number - v2.5.0 (01d276cb)
|
||||
* incrementing version number - v2.4.5 (dd3e1a28)
|
||||
* incrementing version number - v2.4.4 (d5525c87)
|
||||
* incrementing version number - v2.4.3 (9c647c6c)
|
||||
* incrementing version number - v2.4.2 (3aa7b855)
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
##### Performance Improvements
|
||||
|
||||
* speed up build (dd4e9cce)
|
||||
|
||||
#### v2.5.6 (2022-10-13)
|
||||
|
||||
##### Chores
|
||||
|
||||
* incrementing version number - v2.5.5 (3509ed94)
|
||||
* update changelog for v2.5.5 (e7d0040d)
|
||||
* incrementing version number - v2.5.4 (e83260ca)
|
||||
* incrementing version number - v2.5.3 (7e922936)
|
||||
* incrementing version number - v2.5.2 (babcd17e)
|
||||
* incrementing version number - v2.5.1 (ce3aa950)
|
||||
* incrementing version number - v2.5.0 (01d276cb)
|
||||
* incrementing version number - v2.4.5 (dd3e1a28)
|
||||
* incrementing version number - v2.4.4 (d5525c87)
|
||||
* incrementing version number - v2.4.3 (9c647c6c)
|
||||
* incrementing version number - v2.4.2 (3aa7b855)
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
* use admin:groups priv for groups (#10960) (b879b6a0)
|
||||
* https://github.com/NodeBB/NodeBB/issues/10525 (e35b0a86)
|
||||
|
||||
#### v2.5.5 (2022-10-11)
|
||||
|
||||
##### Chores
|
||||
|
||||
* up plugins (b91ef6dd)
|
||||
* incrementing version number - v2.5.4 (e83260ca)
|
||||
* update changelog for v2.5.4 (aabf073c)
|
||||
* incrementing version number - v2.5.3 (7e922936)
|
||||
* incrementing version number - v2.5.2 (babcd17e)
|
||||
* incrementing version number - v2.5.1 (ce3aa950)
|
||||
* incrementing version number - v2.5.0 (01d276cb)
|
||||
* incrementing version number - v2.4.5 (dd3e1a28)
|
||||
* incrementing version number - v2.4.4 (d5525c87)
|
||||
* incrementing version number - v2.4.3 (9c647c6c)
|
||||
* incrementing version number - v2.4.2 (3aa7b855)
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
#### v2.5.4 (2022-10-11)
|
||||
|
||||
##### Chores
|
||||
|
||||
* 🤔 (7240e8ce)
|
||||
* incrementing version number - v2.5.3 (7e922936)
|
||||
* update changelog for v2.5.3 (fdf240f6)
|
||||
* incrementing version number - v2.5.2 (babcd17e)
|
||||
* incrementing version number - v2.5.1 (ce3aa950)
|
||||
* incrementing version number - v2.5.0 (01d276cb)
|
||||
* incrementing version number - v2.4.5 (dd3e1a28)
|
||||
* incrementing version number - v2.4.4 (d5525c87)
|
||||
* incrementing version number - v2.4.3 (9c647c6c)
|
||||
* incrementing version number - v2.4.2 (3aa7b855)
|
||||
* incrementing version number - v2.4.1 (60cbd148)
|
||||
* incrementing version number - v2.4.0 (4834cde3)
|
||||
* incrementing version number - v2.3.1 (d2425942)
|
||||
* incrementing version number - v2.3.0 (046ea120)
|
||||
|
||||
##### Continuous Integration
|
||||
|
||||
* add minimum GitHub token permissions for workflows Signed-off-by: Ashish Kurmi <akurmi@stepsecurity.io> (fe0020fb)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
* EEXISTS error on linux if plugin/theme overrides core js file (ebd5dcc6)
|
||||
* category ordering add test (177d9048)
|
||||
* crash in category drag, closes #10932 (989b55d0)
|
||||
* broken flag history on flag update (803398e9)
|
||||
* scroll to post if theme doesn't have top navbar (aad0a618)
|
||||
* add lru-cache to checked packages, to fix upgrade issue with lru-cache (14515f60)
|
||||
|
||||
#### v2.5.3 (2022-09-19)
|
||||
|
||||
##### Chores
|
||||
|
||||
@@ -138,10 +138,9 @@ module.exports = function (grunt) {
|
||||
});
|
||||
const build = require('./src/meta/build');
|
||||
if (!grunt.option('skip')) {
|
||||
await build.build(true, { webpack: false });
|
||||
await build.build(true, { watch: true });
|
||||
}
|
||||
run();
|
||||
await build.webpack({ watch: true });
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
"maximumRelatedTopics": 0,
|
||||
"disableEmailSubscriptions": 0,
|
||||
"emailConfirmInterval": 10,
|
||||
"emailConfirmExpiry": 24,
|
||||
"removeEmailNotificationImages": 0,
|
||||
"sendValidationEmail": 1,
|
||||
"includeUnverifiedEmails": 0,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPL-3.0",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "2.5.3",
|
||||
"version": "2.5.7",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -112,6 +112,7 @@
|
||||
"pg-cursor": "2.7.4",
|
||||
"postcss": "8.4.18",
|
||||
"postcss-clean": "1.2.0",
|
||||
"progress-webpack-plugin": "1.0.16",
|
||||
"prompt": "1.3.0",
|
||||
"ioredis": "5.2.3",
|
||||
"request": "2.88.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "المصادقة",
|
||||
"email-confirm-interval": "لا يمكن للمستخدم إعادة إرسال رسالة تأكيد البريد الالكتروني حتى مرور",
|
||||
"email-confirm-email2": "دقائق",
|
||||
"email-confirm-interval2": "دقائق",
|
||||
"allow-login-with": "السماح بتسجيل الدخول باستخدام",
|
||||
"allow-login-with.username-email": "اسم المستخدم أو البريد الالكتروني",
|
||||
"allow-login-with.username": "اسم المستخدم فقط",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Удостоверяване",
|
||||
"email-confirm-interval": "Потребителят не може да изпраща повторно е-писмото за потвърждение, преди да са минали",
|
||||
"email-confirm-email2": "минути",
|
||||
"email-confirm-interval2": "минути",
|
||||
"allow-login-with": "Позволяване на вписването чрез",
|
||||
"allow-login-with.username-email": "Потребителско име или е-поща",
|
||||
"allow-login-with.username": "Само потребителско име",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Ověření",
|
||||
"email-confirm-interval": "Uživatel nesmí požádat o znovu zaslání potvrzujícího e-mailu do",
|
||||
"email-confirm-email2": "minut uplynulo",
|
||||
"email-confirm-interval2": "minut uplynulo",
|
||||
"allow-login-with": "Povolit přihlášení pomocí",
|
||||
"allow-login-with.username-email": "Uživatelské jméno nebo e-mail",
|
||||
"allow-login-with.username": "Pouze uživatelské jméno",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentifizierung",
|
||||
"email-confirm-interval": "Der Benutzer kann für ",
|
||||
"email-confirm-email2": " Minuten keine Bestätigungsmail erneut senden.",
|
||||
"email-confirm-interval2": " Minuten keine Bestätigungsmail erneut senden.",
|
||||
"allow-login-with": "Erlaube Login mit",
|
||||
"allow-login-with.username-email": "Benutzername oder E-Mail",
|
||||
"allow-login-with.username": "Nur Benutzername",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
"from": "From Name",
|
||||
"from-help": "The from name to display in the email.",
|
||||
|
||||
"confirmation-settings": "Confirmation",
|
||||
"confirmation.expiry": "Hours to keep email confirmation link valid",
|
||||
|
||||
"smtp-transport": "SMTP Transport",
|
||||
"smtp-transport.enabled": "Enable SMTP Transport",
|
||||
"smtp-transport-help": "You can select from a list of well-known services or enter a custom one.",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Autenticación",
|
||||
"email-confirm-interval": "El usuario no puede re-enviar una confirmación por email hasta",
|
||||
"email-confirm-email2": "minutos han pasado",
|
||||
"email-confirm-interval2": "minutos han pasado",
|
||||
"allow-login-with": "Permitir login con",
|
||||
"allow-login-with.username-email": "Nombre de usuario o Email",
|
||||
"allow-login-with.username": "Solo Nombre de Usuario",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentification",
|
||||
"email-confirm-interval": "Les utilisateurs ne peuvent pas demander un e-mail de vérification avant",
|
||||
"email-confirm-email2": "minutes se sont écoulées",
|
||||
"email-confirm-interval2": "minutes se sont écoulées",
|
||||
"allow-login-with": "Autoriser l'identification avec",
|
||||
"allow-login-with.username-email": "Nom d'utilisateur ou e-mail",
|
||||
"allow-login-with.username": "Nom d'utilisateur uniquement",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "אימות",
|
||||
"email-confirm-interval": "המשתמש לא יוכל לשלוח הודעת אישור מייל עד שיחלוף",
|
||||
"email-confirm-email2": "דקות",
|
||||
"email-confirm-interval2": "דקות",
|
||||
"allow-login-with": "אפשר התחברות עם",
|
||||
"allow-login-with.username-email": "שם משתמש או סיסמא",
|
||||
"allow-login-with.username": "שם משתמש בלבד",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Autentifikacija",
|
||||
"email-confirm-interval": "Korisnik ne može ponovno poslati potvrdni email do ",
|
||||
"email-confirm-email2": "prošlo je minuta",
|
||||
"email-confirm-interval2": "prošlo je minuta",
|
||||
"allow-login-with": "Dozvoli prijavu sa",
|
||||
"allow-login-with.username-email": "Korisničko ime ili Email",
|
||||
"allow-login-with.username": "Korisničko ime",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Hitelesítés",
|
||||
"email-confirm-interval": "A felhasználó nem küldetheti újra az emailt ameddig nem telt el",
|
||||
"email-confirm-email2": "perc",
|
||||
"email-confirm-interval2": "perc",
|
||||
"allow-login-with": "Bejelentkezés engedélyezése ezzel:",
|
||||
"allow-login-with.username-email": "Felhasználónév vagy email cím",
|
||||
"allow-login-with.username": "Csak felhasználónév",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Autenticazione",
|
||||
"email-confirm-interval": "L'utente non può mandare una nuova email di conferma fino a",
|
||||
"email-confirm-email2": "minuti trascorsi",
|
||||
"email-confirm-interval2": "minuti trascorsi",
|
||||
"allow-login-with": "Consenti l'accesso con",
|
||||
"allow-login-with.username-email": "Username o Email",
|
||||
"allow-login-with.username": "Solo Username",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "認証",
|
||||
"email-confirm-interval": "ユーザーが確認するまでEメールを再送信しない",
|
||||
"email-confirm-email2": "分経過",
|
||||
"email-confirm-interval2": "分経過",
|
||||
"allow-login-with": "ログインを許可",
|
||||
"allow-login-with.username-email": "ユーザー名または電子メール",
|
||||
"allow-login-with.username": "ユーザー名のみ",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "인증",
|
||||
"email-confirm-interval": "사용자는",
|
||||
"email-confirm-email2": "분이 지나기 전까지 인증 메일 재요청이 불가능합니다.",
|
||||
"email-confirm-interval2": "분이 지나기 전까지 인증 메일 재요청이 불가능합니다.",
|
||||
"allow-login-with": "로그인 허용 수단",
|
||||
"allow-login-with.username-email": "사용자명 또는 이메일",
|
||||
"allow-login-with.username": "사용자명",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Autentifikācija",
|
||||
"email-confirm-interval": "Lietotājs nevar atkārtoti nosūtīt apstiprinājuma e-pastu pirms",
|
||||
"email-confirm-email2": "minūtes nav pagājušas",
|
||||
"email-confirm-interval2": "minūtes nav pagājušas",
|
||||
"allow-login-with": "Ielogoties",
|
||||
"allow-login-with.username-email": "Ar lietotājvārdu vai e-pasta adresi",
|
||||
"allow-login-with.username": "Tikai ar lietotājvārdu",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Autentisering ",
|
||||
"email-confirm-interval": "Brukeren kan ikke sende en bekreftelses-e-post på nytt før",
|
||||
"email-confirm-email2": "minutter har gått",
|
||||
"email-confirm-interval2": "minutter har gått",
|
||||
"allow-login-with": "Tillat innlogging med",
|
||||
"allow-login-with.username-email": "Brukernavn eller e-post",
|
||||
"allow-login-with.username": "Kun brukernavn",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Uwierzytelnianie",
|
||||
"email-confirm-interval": "Użytkownik nie może ponownie wysłać e-maila z potwierdzeniem, dopóki nie minie",
|
||||
"email-confirm-email2": "minut",
|
||||
"email-confirm-interval2": "minut",
|
||||
"allow-login-with": "Zezwalaj na logowanie przy użyciu",
|
||||
"allow-login-with.username-email": "Nazwy użytkownika lub adresu email",
|
||||
"allow-login-with.username": "Tylko nazwy użytkownika",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Autenticação",
|
||||
"email-confirm-interval": "O usuário não pode reenviar um e-mail de confirmação até",
|
||||
"email-confirm-email2": "minutos decorridos",
|
||||
"email-confirm-interval2": "minutos decorridos",
|
||||
"allow-login-with": "Permitir login com",
|
||||
"allow-login-with.username-email": "Nome de Usuário ou E-mail",
|
||||
"allow-login-with.username": "Apenas Nome de Usuário",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Autenticação",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Permitir início de sessão com",
|
||||
"allow-login-with.username-email": "Nome de Utilizador ou E-mail",
|
||||
"allow-login-with.username": "Nome de Utilizador Apenas",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Авторизация",
|
||||
"email-confirm-interval": "Пользователь не сможет снова запросить код подтверждения, пока не пройдёт",
|
||||
"email-confirm-email2": "минут",
|
||||
"email-confirm-interval2": "минут",
|
||||
"allow-login-with": "Разрешить вход с помощью",
|
||||
"allow-login-with.username-email": "Имени пользователя или адреса электронной почты",
|
||||
"allow-login-with.username": "Только имени пользователя",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Overenie",
|
||||
"email-confirm-interval": "Používateľ nesmie požiadať o znovu odoslanie potvrdzujúceho e-mailu do",
|
||||
"email-confirm-email2": "minút uplynulo",
|
||||
"email-confirm-interval2": "minút uplynulo",
|
||||
"allow-login-with": "Povoliť prihlásenie pomocou",
|
||||
"allow-login-with.username-email": "Používateľské meno alebo e-mail",
|
||||
"allow-login-with.username": "Iba používateľské meno",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Preverjanje pristnosti",
|
||||
"email-confirm-interval": "Uporabnik morda ne bo mogel znova poslati potrditvenega e-poštnega sporočila, dokler",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Dovoli prijavo z",
|
||||
"allow-login-with.username-email": "Uporabniško ime ali e-poštni naslov",
|
||||
"allow-login-with.username": "Samo uporabniško ime",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Auntentifikacija",
|
||||
"email-confirm-interval": "Korisnik možda neće moći da ponovo pošalje email konfirmaciju sve dok",
|
||||
"email-confirm-email2": "minuta je prošlo",
|
||||
"email-confirm-interval2": "minuta je prošlo",
|
||||
"allow-login-with": "Dozvoli login sa",
|
||||
"allow-login-with.username-email": "Korisničko ime ili Email",
|
||||
"allow-login-with.username": "Samo korisničko ime",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Authentication",
|
||||
"email-confirm-interval": "User may not resend a confirmation email until",
|
||||
"email-confirm-email2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"allow-login-with": "Allow login with",
|
||||
"allow-login-with.username-email": "Username or Email",
|
||||
"allow-login-with.username": "Username Only",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Kimlik Doğrulama",
|
||||
"email-confirm-interval": "Kullanıcı onay e-postasını tekrar gönderemez",
|
||||
"email-confirm-email2": "dakika geçti",
|
||||
"email-confirm-interval2": "dakika geçti",
|
||||
"allow-login-with": "Girişe izin ver:",
|
||||
"allow-login-with.username-email": "Kullanıcı Adı veya E-posta",
|
||||
"allow-login-with.username": "Sadece kullanıcı adı",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Аутентифікація",
|
||||
"email-confirm-interval": "Користувач не може повторно надіслати підтвердження електронної пошти поки не мине",
|
||||
"email-confirm-email2": "хвилин",
|
||||
"email-confirm-interval2": "хвилин",
|
||||
"allow-login-with": "Дозволити вхід використовуючи",
|
||||
"allow-login-with.username-email": "Ім'я користувача або електронну пошту",
|
||||
"allow-login-with.username": "Тільки ім'я користувача",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Xác thực",
|
||||
"email-confirm-interval": "Người dùng không thể gửi lại email xác nhận cho đến khi",
|
||||
"email-confirm-email2": "phút đã trôi qua",
|
||||
"email-confirm-interval2": "phút đã trôi qua",
|
||||
"allow-login-with": "Cho phép đăng nhập với",
|
||||
"allow-login-with.username-email": "Tên Đăng Nhập hoặc Email",
|
||||
"allow-login-with.username": "Chỉ Tên Đăng Nhập",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "验证",
|
||||
"email-confirm-interval": "用户无法重新发送电子邮箱激活直到",
|
||||
"email-confirm-email2": "分钟已经过",
|
||||
"email-confirm-interval2": "分钟已经过",
|
||||
"allow-login-with": "允许使用何种登录名",
|
||||
"allow-login-with.username-email": "用户名或者邮箱",
|
||||
"allow-login-with.username": "仅限用户名",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "驗證",
|
||||
"email-confirm-interval": "使用者無法重新發送電子信箱確認信直到",
|
||||
"email-confirm-email2": "分鐘已經過",
|
||||
"email-confirm-interval2": "分鐘已經過",
|
||||
"allow-login-with": "允許使用何種登入名",
|
||||
"allow-login-with.username-email": "使用者名或者電子信箱",
|
||||
"allow-login-with.username": "僅限使用者名",
|
||||
|
||||
@@ -45,7 +45,7 @@ define('iconSelect', ['benchpress', 'bootbox'], function (Benchpress, bootbox) {
|
||||
label: 'Select',
|
||||
className: 'btn-primary',
|
||||
callback: function () {
|
||||
const iconClass = $('.bootbox .selected').attr('class');
|
||||
const iconClass = $('.bootbox .selected').attr('class') || `fa fa-${$('.bootbox #fa-filter').val()}`;
|
||||
const categoryIconClass = $('<div></div>').addClass(iconClass).removeClass('fa').removeClass('selected')
|
||||
.attr('class');
|
||||
const searchElVal = picker.find('input').val();
|
||||
|
||||
@@ -38,12 +38,7 @@ define('messages', ['bootbox', 'translator', 'storage', 'alerts', 'hooks'], func
|
||||
msg.message = message || '[[error:email-not-confirmed]]';
|
||||
msg.clickfn = function () {
|
||||
alerts.remove('email_confirm');
|
||||
socket.emit('user.emailConfirm', {}, function (err) {
|
||||
if (err) {
|
||||
return alerts.error(err);
|
||||
}
|
||||
alerts.success('[[notifications:email-confirm-sent]]');
|
||||
});
|
||||
ajaxify.go('/me/edit/email');
|
||||
};
|
||||
alerts.alert(msg);
|
||||
} else if (!app.user['email:confirmed'] && app.user.isEmailConfirmSent) {
|
||||
|
||||
@@ -215,14 +215,14 @@ async function isOwner(caller, groupName) {
|
||||
if (typeof groupName !== 'string') {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
const [isAdmin, isGlobalModerator, isOwner, group] = await Promise.all([
|
||||
user.isAdministrator(caller.uid),
|
||||
const [hasAdminPrivilege, isGlobalModerator, isOwner, group] = await Promise.all([
|
||||
privileges.admin.can('admin:groups', caller.uid),
|
||||
user.isGlobalModerator(caller.uid),
|
||||
groups.ownership.isOwner(caller.uid, groupName),
|
||||
groups.getGroupData(groupName),
|
||||
]);
|
||||
|
||||
const check = isOwner || isAdmin || (isGlobalModerator && !group.system);
|
||||
const check = isOwner || hasAdminPrivilege || (isGlobalModerator && !group.system);
|
||||
if (!check) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
@@ -342,6 +342,14 @@ async function createAdmin() {
|
||||
try {
|
||||
User.isPasswordValid(results.password);
|
||||
} catch (err) {
|
||||
const [namespace, key] = err.message.slice(2, -2).split(':', 2);
|
||||
if (namespace && key && err.message.startsWith('[[') && err.message.endsWith(']]')) {
|
||||
const lang = require(path.join(__dirname, `../public/language/en-GB/${namespace}`));
|
||||
if (lang && lang[key]) {
|
||||
err.message = lang[key];
|
||||
}
|
||||
}
|
||||
|
||||
winston.warn(`Password error, please try again. ${err.message}`);
|
||||
return await retryPassword(results);
|
||||
}
|
||||
|
||||
@@ -77,20 +77,36 @@ async function beforeBuild(targets) {
|
||||
|
||||
const allTargets = Object.keys(targetHandlers).filter(name => typeof targetHandlers[name] === 'function');
|
||||
|
||||
async function buildTargets(targets, parallel) {
|
||||
async function buildTargets(targets, parallel, options) {
|
||||
const length = Math.max(...targets.map(name => name.length));
|
||||
|
||||
if (parallel) {
|
||||
const jsTargets = targets.filter(target => targetHandlers.javascript.includes(target));
|
||||
const otherTargets = targets.filter(target => !targetHandlers.javascript.includes(target));
|
||||
async function buildJSTargets() {
|
||||
await Promise.all(
|
||||
targets.map(
|
||||
jsTargets.map(
|
||||
target => step(target, parallel, `${_.padStart(target, length)} `)
|
||||
)
|
||||
);
|
||||
// run webpack after jstargets are done, no need to wait for css/templates etc.
|
||||
if (options.webpack || options.watch) {
|
||||
await exports.webpack(options);
|
||||
}
|
||||
}
|
||||
if (parallel) {
|
||||
await Promise.all([
|
||||
buildJSTargets(),
|
||||
...otherTargets.map(
|
||||
target => step(target, parallel, `${_.padStart(target, length)} `)
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
for (const target of targets) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await step(target, parallel, `${_.padStart(target, length)} `);
|
||||
}
|
||||
if (options.webpack || options.watch) {
|
||||
await exports.webpack(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,11 +191,7 @@ exports.build = async function (targets, options) {
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
await buildTargets(targets, !series);
|
||||
|
||||
if (options.webpack) {
|
||||
await exports.webpack(options);
|
||||
}
|
||||
await buildTargets(targets, !series, options);
|
||||
|
||||
const totalTime = (Date.now() - startTime) / 1000;
|
||||
await cacheBuster.write();
|
||||
|
||||
@@ -27,14 +27,6 @@ JS.scripts = {
|
||||
modules: { },
|
||||
};
|
||||
|
||||
async function linkIfLinux(srcPath, destPath) {
|
||||
if (process.platform === 'win32') {
|
||||
await fs.promises.copyFile(srcPath, destPath);
|
||||
} else {
|
||||
await file.link(srcPath, destPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
const basePath = path.resolve(__dirname, '../..');
|
||||
|
||||
async function linkModules() {
|
||||
@@ -55,7 +47,7 @@ async function linkModules() {
|
||||
if (stats.isDirectory()) {
|
||||
await file.linkDirs(srcPath, destPath, true);
|
||||
} else {
|
||||
await linkIfLinux(srcPath, destPath);
|
||||
await fs.promises.copyFile(srcPath, destPath);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -42,13 +42,15 @@ async function isOwner(socket, data) {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
const results = await utils.promiseParallel({
|
||||
isAdmin: await user.isAdministrator(socket.uid),
|
||||
isGlobalModerator: await user.isGlobalModerator(socket.uid),
|
||||
isOwner: await groups.ownership.isOwner(socket.uid, data.groupName),
|
||||
group: await groups.getGroupData(data.groupName),
|
||||
hasAdminPrivilege: privileges.admin.can('admin:groups', socket.uid),
|
||||
isGlobalModerator: user.isGlobalModerator(socket.uid),
|
||||
isOwner: groups.ownership.isOwner(socket.uid, data.groupName),
|
||||
group: groups.getGroupData(data.groupName),
|
||||
});
|
||||
|
||||
const isOwner = results.isOwner || results.isAdmin || (results.isGlobalModerator && !results.group.system);
|
||||
const isOwner = results.isOwner ||
|
||||
results.hasAdminPrivilege ||
|
||||
(results.isGlobalModerator && !results.group.system);
|
||||
if (!isOwner) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
@@ -220,15 +222,15 @@ SocketGroups.loadMoreMembers = async (socket, data) => {
|
||||
};
|
||||
|
||||
async function canSearchMembers(uid, groupName) {
|
||||
const [isHidden, isMember, isAdmin, isGlobalMod, viewGroups] = await Promise.all([
|
||||
const [isHidden, isMember, hasAdminPrivilege, isGlobalMod, viewGroups] = await Promise.all([
|
||||
groups.isHidden(groupName),
|
||||
groups.isMember(uid, groupName),
|
||||
user.isAdministrator(uid),
|
||||
privileges.admin.can('admin:groups', uid),
|
||||
user.isGlobalModerator(uid),
|
||||
privileges.global.can('view:groups', uid),
|
||||
]);
|
||||
|
||||
if (!viewGroups || (isHidden && !isMember && !isAdmin && !isGlobalMod)) {
|
||||
if (!viewGroups || (isHidden && !isMember && !hasAdminPrivilege && !isGlobalMod)) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
}
|
||||
@@ -268,11 +270,11 @@ async function canModifyGroup(uid, groupName) {
|
||||
const results = await utils.promiseParallel({
|
||||
isOwner: groups.ownership.isOwner(uid, groupName),
|
||||
system: groups.getGroupField(groupName, 'system'),
|
||||
isAdmin: user.isAdministrator(uid),
|
||||
hasAdminPrivilege: privileges.admin.can('admin:groups', uid),
|
||||
isGlobalMod: user.isGlobalModerator(uid),
|
||||
});
|
||||
|
||||
if (!(results.isOwner || results.isAdmin || (results.isGlobalMod && !results.system))) {
|
||||
if (!(results.isOwner || results.hasAdminPrivilege || (results.isGlobalMod && !results.system))) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ const db = require('../database');
|
||||
const userController = require('../controllers/user');
|
||||
const privileges = require('../privileges');
|
||||
const utils = require('../utils');
|
||||
const sockets = require('.');
|
||||
|
||||
const SocketUser = module.exports;
|
||||
|
||||
@@ -25,6 +26,8 @@ require('./user/picture')(SocketUser);
|
||||
require('./user/registration')(SocketUser);
|
||||
|
||||
SocketUser.emailConfirm = async function (socket) {
|
||||
sockets.warnDeprecated(socket, 'HTTP 302 /me/edit/email');
|
||||
|
||||
if (!socket.uid) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
@@ -32,7 +35,6 @@ SocketUser.emailConfirm = async function (socket) {
|
||||
return await user.email.sendValidationEmail(socket.uid);
|
||||
};
|
||||
|
||||
|
||||
// Password Reset
|
||||
SocketUser.reset = {};
|
||||
|
||||
|
||||
@@ -49,12 +49,17 @@ UserEmail.isValidationPending = async (uid, email) => {
|
||||
|
||||
if (email) {
|
||||
const confirmObj = await db.getObject(`confirm:${code}`);
|
||||
return confirmObj && email === confirmObj.email;
|
||||
return !!(confirmObj && email === confirmObj.email);
|
||||
}
|
||||
|
||||
return !!code;
|
||||
};
|
||||
|
||||
UserEmail.getValidationExpiry = async (uid) => {
|
||||
const pending = await UserEmail.isValidationPending(uid);
|
||||
return pending ? db.pttl(`confirm:byUid:${uid}`) : null;
|
||||
};
|
||||
|
||||
UserEmail.expireValidation = async (uid) => {
|
||||
const code = await db.get(`confirm:byUid:${uid}`);
|
||||
await db.deleteAll([
|
||||
@@ -63,6 +68,19 @@ UserEmail.expireValidation = async (uid) => {
|
||||
]);
|
||||
};
|
||||
|
||||
UserEmail.canSendValidation = async (uid, email) => {
|
||||
const pending = UserEmail.isValidationPending(uid, email);
|
||||
if (!pending) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ttl = await UserEmail.getValidationExpiry(uid);
|
||||
const max = meta.config.emailConfirmExpiry * 60 * 60 * 1000;
|
||||
const interval = meta.config.emailConfirmInterval * 60 * 1000;
|
||||
|
||||
return ttl + interval < max;
|
||||
};
|
||||
|
||||
UserEmail.sendValidationEmail = async function (uid, options) {
|
||||
/*
|
||||
* Options:
|
||||
@@ -88,7 +106,7 @@ UserEmail.sendValidationEmail = async function (uid, options) {
|
||||
const confirm_code = utils.generateUUID();
|
||||
const confirm_link = `${nconf.get('url')}/confirm/${confirm_code}`;
|
||||
|
||||
const emailInterval = meta.config.emailConfirmInterval;
|
||||
const { emailConfirmInterval, emailConfirmExpiry } = meta.config;
|
||||
|
||||
// If no email passed in (default), retrieve email from uid
|
||||
if (!options.email || !options.email.length) {
|
||||
@@ -97,12 +115,9 @@ UserEmail.sendValidationEmail = async function (uid, options) {
|
||||
if (!options.email) {
|
||||
return;
|
||||
}
|
||||
let sent = false;
|
||||
if (!options.force) {
|
||||
sent = await UserEmail.isValidationPending(uid, options.email);
|
||||
}
|
||||
if (sent) {
|
||||
throw new Error(`[[error:confirm-email-already-sent, ${emailInterval}]]`);
|
||||
|
||||
if (!options.force && !await UserEmail.canSendValidation(uid, options.email)) {
|
||||
throw new Error(`[[error:confirm-email-already-sent, ${emailConfirmInterval}]]`);
|
||||
}
|
||||
|
||||
const username = await user.getUserField(uid, 'username');
|
||||
@@ -119,13 +134,13 @@ UserEmail.sendValidationEmail = async function (uid, options) {
|
||||
|
||||
await UserEmail.expireValidation(uid);
|
||||
await db.set(`confirm:byUid:${uid}`, confirm_code);
|
||||
await db.pexpireAt(`confirm:byUid:${uid}`, Date.now() + (emailInterval * 60 * 1000));
|
||||
await db.pexpire(`confirm:byUid:${uid}`, emailConfirmExpiry * 24 * 60 * 60 * 1000);
|
||||
|
||||
await db.setObject(`confirm:${confirm_code}`, {
|
||||
email: options.email.toLowerCase(),
|
||||
uid: uid,
|
||||
});
|
||||
await db.expireAt(`confirm:${confirm_code}`, Math.floor((Date.now() / 1000) + (60 * 60 * 24)));
|
||||
await db.pexpire(`confirm:${confirm_code}`, emailConfirmExpiry * 24 * 60 * 60 * 1000);
|
||||
|
||||
winston.verbose(`[user/email] Validation email for uid ${uid} sent to ${options.email}`);
|
||||
events.log({
|
||||
|
||||
@@ -42,10 +42,10 @@ Interstitials.email = async (data) => {
|
||||
callback: async (userData, formData) => {
|
||||
// Validate and send email confirmation
|
||||
if (userData.uid) {
|
||||
const [isPasswordCorrect, canEdit, current, { allowed, error }] = await Promise.all([
|
||||
const [isPasswordCorrect, canEdit, { email: current, 'email:confirmed': confirmed }, { allowed, error }] = await Promise.all([
|
||||
user.isPasswordCorrect(userData.uid, formData.password, data.req.ip),
|
||||
privileges.users.canEdit(data.req.uid, userData.uid),
|
||||
user.getUserField(userData.uid, 'email'),
|
||||
user.getUserFields(userData.uid, ['email', 'email:confirmed']),
|
||||
plugins.hooks.fire('filter:user.saveEmail', {
|
||||
uid: userData.uid,
|
||||
email: formData.email,
|
||||
@@ -64,8 +64,13 @@ Interstitials.email = async (data) => {
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
// Handle errors when setting to same email (unconfirmed accts only)
|
||||
if (formData.email === current) {
|
||||
throw new Error('[[error:email-nochange]]');
|
||||
if (confirmed) {
|
||||
throw new Error('[[error:email-nochange]]');
|
||||
} else if (await user.email.canSendValidation(userData.uid, current)) {
|
||||
throw new Error(`[[error:confirm-email-already-sent, ${meta.config.emailConfirmInterval}]]`);
|
||||
}
|
||||
}
|
||||
|
||||
// Admins editing will auto-confirm, unless editing their own email
|
||||
|
||||
@@ -28,29 +28,6 @@
|
||||
</div>
|
||||
<p class="help-block">[[admin/settings/email:require-email-address-warning]]</p>
|
||||
|
||||
<div class="checkbox">
|
||||
<label for="sendValidationEmail" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" id="sendValidationEmail" data-field="sendValidationEmail" name="sendValidationEmail" />
|
||||
<span class="mdl-switch__label">[[admin/settings/email:send-validation-email]]</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label for="includeUnverifiedEmails" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" id="includeUnverifiedEmails" data-field="includeUnverifiedEmails" name="includeUnverifiedEmails" />
|
||||
<span class="mdl-switch__label">[[admin/settings/email:include-unverified-emails]]</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="help-block">[[admin/settings/email:include-unverified-warning]]</p>
|
||||
|
||||
<div class="checkbox">
|
||||
<label for="emailPrompt" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" id="emailPrompt" data-field="emailPrompt" name="emailPrompt" />
|
||||
<span class="mdl-switch__label">[[admin/settings/email:prompt]]</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="help-block">[[admin/settings/email:prompt-help]]</p>
|
||||
|
||||
<div class="checkbox">
|
||||
<label for="sendEmailToBanned" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" id="sendEmailToBanned" data-field="sendEmailToBanned" name="sendEmailToBanned" />
|
||||
@@ -68,6 +45,45 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/email:confirmation-settings]]</div>
|
||||
<div class="col-sm-10 col-xs-12">
|
||||
<div class="form-group form-inline">
|
||||
<label for="emailConfirmInterval">[[admin/settings/user:email-confirm-interval]]</label>
|
||||
<input class="form-control" data-field="emailConfirmInterval" type="number" id="emailConfirmInterval" placeholder="10" />
|
||||
<label for="emailConfirmInterval">[[admin/settings/user:email-confirm-interval2]]</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="emailConfirmExpiry">[[admin/settings/email:confirmation.expiry]]</label>
|
||||
<input class="form-control" data-field="emailConfirmExpiry" type="number" id="emailConfirmExpiry" placeholder="24" />
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label for="sendValidationEmail" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" id="sendValidationEmail" data-field="sendValidationEmail" name="sendValidationEmail" />
|
||||
<span class="mdl-switch__label">[[admin/settings/email:send-validation-email]]</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label for="includeUnverifiedEmails" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" id="includeUnverifiedEmails" data-field="includeUnverifiedEmails" name="includeUnverifiedEmails" />
|
||||
<span class="mdl-switch__label">[[admin/settings/email:include-unverified-emails]]</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="help-block">[[admin/settings/email:include-unverified-warning]]</p>
|
||||
|
||||
<div class="checkbox">
|
||||
<label for="emailPrompt" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" id="emailPrompt" data-field="emailPrompt" name="emailPrompt" />
|
||||
<span class="mdl-switch__label">[[admin/settings/email:prompt]]</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="help-block">[[admin/settings/email:prompt-help]]</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/email:subscriptions]]</div>
|
||||
<div class="col-sm-10 col-xs-12">
|
||||
|
||||
@@ -4,13 +4,6 @@
|
||||
<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/user:authentication]]</div>
|
||||
<div class="col-sm-10 col-xs-12">
|
||||
<form role="form">
|
||||
<div class="form-group form-inline">
|
||||
<label for="emailConfirmInterval">[[admin/settings/user:email-confirm-interval]]</label>
|
||||
<input class="form-control" data-field="emailConfirmInterval" type="number" id="emailConfirmInterval" placeholder="Default: 10"
|
||||
value="10" />
|
||||
<label for="emailConfirmInterval">[[admin/settings/user:email-confirm-email2]]</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="allowLoginWith">[[admin/settings/user:allow-login-with]]</label>
|
||||
<select id="allowLoginWith" class="form-control" data-field="allowLoginWith">
|
||||
|
||||
@@ -1759,11 +1759,6 @@ describe('User', () => {
|
||||
meta.config.allowAccountDelete = oldValue;
|
||||
});
|
||||
|
||||
it('should send email confirm', async () => {
|
||||
await User.email.expireValidation(testUid);
|
||||
await socketUser.emailConfirm({ uid: testUid }, {});
|
||||
});
|
||||
|
||||
it('should send reset email', (done) => {
|
||||
socketUser.reset.send({ uid: 0 }, 'john@example.com', (err) => {
|
||||
assert.ifError(err);
|
||||
|
||||
@@ -8,8 +8,135 @@ const db = require('../mocks/databasemock');
|
||||
|
||||
const helpers = require('../helpers');
|
||||
|
||||
const meta = require('../../src/meta');
|
||||
const user = require('../../src/user');
|
||||
const groups = require('../../src/groups');
|
||||
const plugins = require('../../src/plugins');
|
||||
const utils = require('../../src/utils');
|
||||
|
||||
describe('email confirmation (library methods)', () => {
|
||||
let uid;
|
||||
async function dummyEmailerHook(data) {
|
||||
// pretend to handle sending emails
|
||||
}
|
||||
|
||||
before(() => {
|
||||
// Attach an emailer hook so related requests do not error
|
||||
plugins.hooks.register('emailer-test', {
|
||||
hook: 'filter:email.send',
|
||||
method: dummyEmailerHook,
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
uid = await user.create({
|
||||
username: utils.generateUUID().slice(0, 10),
|
||||
password: utils.generateUUID(),
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
plugins.hooks.unregister('emailer-test', 'filter:email.send');
|
||||
});
|
||||
|
||||
describe('isValidationPending', () => {
|
||||
it('should return false if user did not request email validation', async () => {
|
||||
const pending = await user.email.isValidationPending(uid);
|
||||
|
||||
assert.strictEqual(pending, false);
|
||||
});
|
||||
|
||||
it('should return false if user did not request email validation (w/ email checking)', async () => {
|
||||
const email = 'test@example.org';
|
||||
const pending = await user.email.isValidationPending(uid, email);
|
||||
|
||||
assert.strictEqual(pending, false);
|
||||
});
|
||||
|
||||
it('should return true if user requested email validation', async () => {
|
||||
const email = 'test@example.org';
|
||||
await user.email.sendValidationEmail(uid, {
|
||||
email,
|
||||
});
|
||||
const pending = await user.email.isValidationPending(uid);
|
||||
|
||||
assert.strictEqual(pending, true);
|
||||
});
|
||||
|
||||
it('should return true if user requested email validation (w/ email checking)', async () => {
|
||||
const email = 'test@example.org';
|
||||
await user.email.sendValidationEmail(uid, {
|
||||
email,
|
||||
});
|
||||
const pending = await user.email.isValidationPending(uid, email);
|
||||
|
||||
assert.strictEqual(pending, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getValidationExpiry', () => {
|
||||
it('should return null if there is no validation available', async () => {
|
||||
const expiry = await user.email.getValidationExpiry(uid);
|
||||
|
||||
assert.strictEqual(expiry, null);
|
||||
});
|
||||
|
||||
it('should return a number smaller than configured expiry if validation available', async () => {
|
||||
const email = 'test@example.org';
|
||||
await user.email.sendValidationEmail(uid, {
|
||||
email,
|
||||
});
|
||||
const expiry = await user.email.getValidationExpiry(uid);
|
||||
|
||||
assert(isFinite(expiry));
|
||||
assert(expiry > 0);
|
||||
assert(expiry <= meta.config.emailConfirmExpiry * 24 * 60 * 60 * 1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('expireValidation', () => {
|
||||
it('should invalidate any confirmation in-progress', async () => {
|
||||
const email = 'test@example.org';
|
||||
await user.email.sendValidationEmail(uid, {
|
||||
email,
|
||||
});
|
||||
await user.email.expireValidation(uid);
|
||||
|
||||
assert.strictEqual(await user.email.isValidationPending(uid), false);
|
||||
assert.strictEqual(await user.email.isValidationPending(uid, email), false);
|
||||
assert.strictEqual(await user.email.canSendValidation(uid, email), true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('canSendValidation', () => {
|
||||
it('should return true if no validation is pending', async () => {
|
||||
const ok = await user.email.canSendValidation(uid, 'test@example.com');
|
||||
|
||||
assert(ok);
|
||||
});
|
||||
|
||||
it('should return false if it has been too soon to re-send confirmation', async () => {
|
||||
const email = 'test@example.org';
|
||||
await user.email.sendValidationEmail(uid, {
|
||||
email,
|
||||
});
|
||||
const ok = await user.email.canSendValidation(uid, 'test@example.com');
|
||||
|
||||
assert.strictEqual(ok, false);
|
||||
});
|
||||
|
||||
it('should return true if it has been long enough to re-send confirmation', async () => {
|
||||
const email = 'test@example.org';
|
||||
await user.email.sendValidationEmail(uid, {
|
||||
email,
|
||||
});
|
||||
await db.pexpire(`confirm:byUid:${uid}`, 1000);
|
||||
const ok = await user.email.canSendValidation(uid, 'test@example.com');
|
||||
|
||||
assert(ok);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('email confirmation (v3 api)', () => {
|
||||
let userObj;
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
|
||||
const { merge } = require('webpack-merge');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const ProgressPlugin = require('progress-webpack-plugin');
|
||||
|
||||
const common = require('./webpack.common');
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production',
|
||||
plugins: [
|
||||
new ProgressPlugin(true),
|
||||
],
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
|
||||
Reference in New Issue
Block a user