From 04f4429f72dc6ad021e56a21973416b5b56ec1bb Mon Sep 17 00:00:00 2001
From: Opliko <25460763+oplik0@users.noreply.github.com>
Date: Fri, 13 Nov 2020 04:23:07 +0100
Subject: [PATCH] Resolve #7514 - optional timer for registration queue (#8796)
* feat: #7514 Optional timer for registration queue
* feat: show minutes in average time
* fix: don't show total number of minutes
* feat: implement requested changes
* fix: just store minutes instead of milliseconds
* feat: set default values
---
install/data/defaults.json | 2 +
.../language/en-GB/admin/settings/user.json | 3 ++
public/language/en-GB/register.json | 53 ++++++++++---------
src/controllers/authentication.js | 10 +++-
src/user/approval.js | 22 +++++++-
src/views/admin/settings/user.tpl | 13 +++++
6 files changed, 75 insertions(+), 28 deletions(-)
diff --git a/install/data/defaults.json b/install/data/defaults.json
index 9cb646a2f8..b6d4fa1907 100644
--- a/install/data/defaults.json
+++ b/install/data/defaults.json
@@ -136,6 +136,8 @@
"necroThreshold": 7,
"categoryWatchState": "watching",
"submitPluginUsage": 1,
+ "showAverageApprovalTime": true,
+ "autoApproveTime": 0,
"maxUserSessions": 10,
"useCompression": 0
}
\ No newline at end of file
diff --git a/public/language/en-GB/admin/settings/user.json b/public/language/en-GB/admin/settings/user.json
index b94dc98870..88f00aefc8 100644
--- a/public/language/en-GB/admin/settings/user.json
+++ b/public/language/en-GB/admin/settings/user.json
@@ -43,6 +43,9 @@
"registration-type.disabled": "No registration",
"registration-type.help": "Normal - Users can register from the /register page.
\nInvite Only - Users can invite others from the users page.
\nAdmin Invite Only - Only administrators can invite others from users and admin/manage/users pages.
\nNo registration - No user registration.
",
"registration-approval-type.help": "Normal - Users are registered immediately.
\nAdmin Approval - User registrations are placed in an approval queue for administrators.
\nAdmin Approval for IPs - Normal for new users, Admin Approval for IP addresses that already have an account.
",
+ "registration-queue-auto-approve-time": "Automatic Approval Time",
+ "registration-queue-auto-approve-time-help": "Hours before user is approved automatically. 0 to disable.",
+ "registration-queue-show-average-time": "Show users average time it takes to approve a new user",
"registration.max-invites": "Maximum Invitations per User",
"max-invites": "Maximum Invitations per User",
"max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"",
diff --git a/public/language/en-GB/register.json b/public/language/en-GB/register.json
index 461295ef5f..c6136b9d0a 100644
--- a/public/language/en-GB/register.json
+++ b/public/language/en-GB/register.json
@@ -1,27 +1,28 @@
{
- "register": "Register",
- "cancel_registration": "Cancel Registration",
- "help.email": "By default, your email will be hidden from the public.",
- "help.username_restrictions": "A unique username between %1 and %2 characters. Others can mention you with @username.",
- "help.minimum_password_length": "Your password's length must be at least %1 characters.",
- "email_address": "Email Address",
- "email_address_placeholder": "Enter Email Address",
- "username": "Username",
- "username_placeholder": "Enter Username",
- "password": "Password",
- "password_placeholder": "Enter Password",
- "confirm_password": "Confirm Password",
- "confirm_password_placeholder": "Confirm Password",
- "register_now_button": "Register Now",
- "alternative_registration": "Alternative Registration",
- "terms_of_use": "Terms of Use",
- "agree_to_terms_of_use": "I agree to the Terms of Use",
- "terms_of_use_error": "You must agree to the Terms of Use",
- "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator.",
- "interstitial.intro": "We require some additional information before we can create your account.",
- "interstitial.errors-found": "We could not complete your registration:",
-
- "gdpr_agree_data": "I consent to the collection and processing of my personal information on this website.",
- "gdpr_agree_email": "I consent to receive digest and notification emails from this website.",
- "gdpr_consent_denied": "You must give consent to this site to collect/process your information, and to send you emails."
-}
\ No newline at end of file
+ "register": "Register",
+ "cancel_registration": "Cancel Registration",
+ "help.email": "By default, your email will be hidden from the public.",
+ "help.username_restrictions": "A unique username between %1 and %2 characters. Others can mention you with @username.",
+ "help.minimum_password_length": "Your password's length must be at least %1 characters.",
+ "email_address": "Email Address",
+ "email_address_placeholder": "Enter Email Address",
+ "username": "Username",
+ "username_placeholder": "Enter Username",
+ "password": "Password",
+ "password_placeholder": "Enter Password",
+ "confirm_password": "Confirm Password",
+ "confirm_password_placeholder": "Confirm Password",
+ "register_now_button": "Register Now",
+ "alternative_registration": "Alternative Registration",
+ "terms_of_use": "Terms of Use",
+ "agree_to_terms_of_use": "I agree to the Terms of Use",
+ "terms_of_use_error": "You must agree to the Terms of Use",
+ "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator.",
+ "registration-queue-average-time": "Our average time for approving memberships is %1 hours %2 minutes.",
+ "registration-queue-auto-approve-time": "Your membership to this forum will be fully activated in up to %1 hours.",
+ "interstitial.intro": "We require some additional information before we can create your account.",
+ "interstitial.errors-found": "We could not complete your registration:",
+ "gdpr_agree_data": "I consent to the collection and processing of my personal information on this website.",
+ "gdpr_agree_email": "I consent to receive digest and notification emails from this website.",
+ "gdpr_consent_denied": "You must give consent to this site to collect/process your information, and to send you emails."
+}
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index 2b59a835a0..7836f8fe17 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -118,7 +118,15 @@ authenticationController.register = async function (req, res) {
async function addToApprovalQueue(req, userData) {
userData.ip = req.ip;
await user.addToApprovalQueue(userData);
- return { message: '[[register:registration-added-to-queue]]' };
+ let message = '[[register:registration-added-to-queue]]';
+ if (meta.config.showAverageApprovalTime) {
+ const average_time = await db.getObjectField('registration:queue:approval:times', 'average');
+ message += ` [[register:registration-queue-average-time, ${Math.floor(average_time / 60)}, ${average_time % 60}]]`;
+ }
+ if (meta.config.autoApproveTime > 0) {
+ message += ` [[register:registration-queue-auto-approve-time, ${meta.config.autoApproveTime}]]`;
+ }
+ return { message: message };
}
authenticationController.registerComplete = function (req, res, next) {
diff --git a/src/user/approval.js b/src/user/approval.js
index 797ef9045f..2476da5cb1 100644
--- a/src/user/approval.js
+++ b/src/user/approval.js
@@ -1,6 +1,7 @@
'use strict';
const validator = require('validator');
+const cronJob = require('cron').CronJob;
const db = require('../database');
const meta = require('../meta');
@@ -12,6 +13,10 @@ const slugify = require('../slugify');
const plugins = require('../plugins');
module.exports = function (User) {
+ new cronJob('0 * * * * *', function () {
+ User.autoApprove();
+ }, null, true);
+
User.addToApprovalQueue = async function (userData) {
userData.username = userData.username.trim();
userData.userslug = slugify(userData.username);
@@ -59,7 +64,7 @@ module.exports = function (User) {
if (!userData) {
throw new Error('[[error:invalid-data]]');
}
-
+ const creation_time = await db.sortedSetScore('registration:queue', username);
const uid = await User.create(userData);
await User.setUserField(uid, 'password', userData.hashedPassword);
await removeFromQueue(username);
@@ -71,6 +76,9 @@ module.exports = function (User) {
template: 'registration_accepted',
uid: uid,
});
+ const total = await db.incrObjectField('registration:queue:approval:times', 'totalTime', Math.floor((Date.now() - creation_time) / 60000));
+ const counter = await db.incrObjectField('registration:queue:approval:times', 'counter', 1);
+ await db.setObjectField('registration:queue:approval:times', 'average', total / counter);
return uid;
};
@@ -140,4 +148,16 @@ module.exports = function (User) {
const uids = await User.getUidsFromSet('ip:' + user.ip + ':uid', 0, -1);
user.ipMatch = await User.getUsersFields(uids, ['uid', 'username', 'picture']);
}
+
+ User.autoApprove = async function () {
+ if (meta.config.autoApproveTime <= 0) {
+ return;
+ }
+ const users = await db.getSortedSetRevRangeWithScores('registration:queue', 0, -1);
+ const now = Date.now();
+ for (const user of users.filter(user => now - user.score >= meta.config.autoApproveTime * 3600000)) {
+ // eslint-disable-next-line no-await-in-loop
+ await User.acceptRegistration(user.value);
+ }
+ };
};
diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl
index 45edc8f5a5..c6f0db9062 100644
--- a/src/views/admin/settings/user.tpl
+++ b/src/views/admin/settings/user.tpl
@@ -186,6 +186,19 @@
[[admin/settings/user:registration-approval-type.help, {config.relative_path}]]
+ [[admin/settings/user:registration-queue-auto-approve-time-help]] +
+