diff --git a/install/data/defaults.json b/install/data/defaults.json index 12acc55fd6..e32f98fd27 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -89,6 +89,7 @@ "min:rep:chat": 0, "min:rep:downvote": 0, "min:rep:upvote": 0, + "min:rep:post-links": 0, "min:rep:flag": 0, "min:rep:profile-picture": 0, "min:rep:cover-picture": 0, diff --git a/public/language/en-GB/admin/settings/reputation.json b/public/language/en-GB/admin/settings/reputation.json index 293aa5b440..53801c6662 100644 --- a/public/language/en-GB/admin/settings/reputation.json +++ b/public/language/en-GB/admin/settings/reputation.json @@ -11,6 +11,7 @@ "downvotes-per-day": "Downvotes per day (set to 0 for unlimited downvotes)", "downvotes-per-user-per-day": "Downvotes per user per day (set to 0 for unlimited downvotes)", "min-rep-chat": "Minimum reputation to send chat messages", + "min-rep-post-links": "Minimum reputation to post links", "min-rep-flag": "Minimum reputation to flag posts", "min-rep-website": "Minimum reputation to add \"Website\" to user profile", "min-rep-aboutme": "Minimum reputation to add \"About me\" to user profile", diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index a374787ea6..af0b72e990 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -199,6 +199,7 @@ "not-enough-reputation-to-chat": "You need %1 reputation to chat", "not-enough-reputation-to-upvote": "You need %1 reputation to upvote", "not-enough-reputation-to-downvote": "You need %1 reputation to downvote", + "not-enough-reputation-to-post-links": "You need %1 reputation to post links", "not-enough-reputation-to-flag": "You need %1 reputation to flag this post", "not-enough-reputation-min-rep-website": "You need %1 reputation to add a website", "not-enough-reputation-min-rep-aboutme": "You need %1 reputation to add an about me", diff --git a/src/api/posts.js b/src/api/posts.js index cb2621ec81..8a8e53ec3a 100644 --- a/src/api/posts.js +++ b/src/api/posts.js @@ -99,6 +99,8 @@ postsAPI.edit = async function (caller, data) { throw new Error(`[[error:content-too-short, ${meta.config.minimumPostLength}]]`); } else if (contentLen > meta.config.maximumPostLength) { throw new Error(`[[error:content-too-long, ${meta.config.maximumPostLength}]]`); + } else if (!await posts.canUserPostContentWithLinks(caller.uid, data.content)) { + throw new Error(`[[error:not-enough-reputation-to-post-links, ${meta.config['min:rep:post-links']}]]`); } data.uid = caller.uid; diff --git a/src/posts/queue.js b/src/posts/queue.js index 2c485fb11e..565e21338b 100644 --- a/src/posts/queue.js +++ b/src/posts/queue.js @@ -85,6 +85,24 @@ module.exports = function (Posts) { postData.data.content = result.postData.content; } + Posts.canUserPostContentWithLinks = async function (uid, content) { + if (!content) { + return false; + } + const [reputation, isPrivileged] = await Promise.all([ + user.getUserField(uid, 'reputation'), + user.isPrivileged(uid), + ]); + + if (!isPrivileged && reputation < meta.config['min:rep:post-links']) { + const parsed = await plugins.hooks.fire('filter:parse.raw', String(content)); + if (parsed.match(/]*>([^<]+)<\/a>/g)) { + return false; + } + } + return true; + }; + Posts.shouldQueue = async function (uid, data) { const [userData, isMemberOfExempt, categoryQueueEnabled] = await Promise.all([ user.getUserFields(uid, ['uid', 'reputation', 'postcount']), @@ -94,7 +112,12 @@ module.exports = function (Posts) { const shouldQueue = meta.config.postQueue && categoryQueueEnabled && !isMemberOfExempt && - (!userData.uid || userData.reputation < meta.config.postQueueReputationThreshold || userData.postcount <= 0); + ( + !userData.uid || + userData.reputation < meta.config.postQueueReputationThreshold || + userData.postcount <= 0 || + !await Posts.canUserPostContentWithLinks(uid, data.content) + ); const result = await plugins.hooks.fire('filter:post.shouldQueue', { shouldQueue: !!shouldQueue, uid: uid, diff --git a/src/topics/create.js b/src/topics/create.js index f962b21ddf..44755ec7ee 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -98,6 +98,9 @@ module.exports = function (Topics) { data.tags = await Topics.filterTags(data.tags, data.cid); if (!data.fromQueue && !isAdmin) { Topics.checkContent(data.content); + if (!await posts.canUserPostContentWithLinks(uid, data.content)) { + throw new Error(`[[error:not-enough-reputation-to-post-links, ${meta.config['min:rep:post-links']}]]`); + } } if (!categoryExists) { @@ -177,6 +180,9 @@ module.exports = function (Topics) { if (!data.fromQueue && !isAdmin) { await user.isReadyToPost(uid, data.cid); Topics.checkContent(data.content); + if (!await posts.canUserPostContentWithLinks(uid, data.content)) { + throw new Error(`[[error:not-enough-reputation-to-post-links, ${meta.config['min:rep:post-links']}]]`); + } } // For replies to scheduled topics, don't have a timestamp older than topic's itself diff --git a/src/views/admin/settings/reputation.tpl b/src/views/admin/settings/reputation.tpl index 6f5796170e..c747f22189 100644 --- a/src/views/admin/settings/reputation.tpl +++ b/src/views/admin/settings/reputation.tpl @@ -53,6 +53,10 @@ +
+ + +