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]] +

+
+
+ +