Date: Thu, 30 Nov 2017 12:11:26 -0500
Subject: [PATCH 092/178] replaced 40px padding with more sensible sizes, fixes
#6138
---
src/views/emails/digest.tpl | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/views/emails/digest.tpl b/src/views/emails/digest.tpl
index 3a50fded7a..2212d8cfda 100644
--- a/src/views/emails/digest.tpl
+++ b/src/views/emails/digest.tpl
@@ -16,13 +16,13 @@
- |
+ |
[[email:greeting_with_name, {username}]],
|
- |
+ |
[[email:digest.notifications, {site_title}]]
|
- |
+ |
[[email:digest.latest_topics, {site_title}]]
From 17c52a515d795f61570ccec10c008331db136c3d Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Thu, 30 Nov 2017 12:24:07 -0500
Subject: [PATCH 093/178] fix incorrect padding/margin values in images in
digest
---
public/src/modules/helpers.js | 8 ++++----
src/views/emails/digest.tpl | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js
index 77a060ed7a..039eeb3250 100644
--- a/public/src/modules/helpers.js
+++ b/public/src/modules/helpers.js
@@ -210,14 +210,14 @@
function renderDigestAvatar(block) {
if (block.teaser) {
if (block.teaser.user.picture) {
- return ' ';
+ return ' ';
}
- return '' + block.teaser.user['icon:text'] + ' ';
+ return '' + block.teaser.user['icon:text'] + ' ';
}
if (block.user.picture) {
- return ' ';
+ return ' ';
}
- return '' + block.user['icon:text'] + ' ';
+ return '' + block.user['icon:text'] + ' ';
}
function userAgentIcons(data) {
diff --git a/src/views/emails/digest.tpl b/src/views/emails/digest.tpl
index 2212d8cfda..38fba1232f 100644
--- a/src/views/emails/digest.tpl
+++ b/src/views/emails/digest.tpl
@@ -29,9 +29,9 @@
-
-
+
- {notifications.user.icon:text}
+ {notifications.user.icon:text}
{notifications.bodyShort}
From 4f2f84e47c90fcbc6828bc2d0682ffb139ac97df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Thu, 30 Nov 2017 12:39:03 -0500
Subject: [PATCH 094/178] #4804
---
src/socket.io/topics.js | 1 +
src/socket.io/topics/merge.js | 27 +++++++++++
src/topics.js | 3 +-
src/topics/merge.js | 33 ++++++++++++++
src/topics/posts.js | 2 +-
test/topics.js | 85 +++++++++++++++++++++++++++++++++++
6 files changed, 149 insertions(+), 2 deletions(-)
create mode 100644 src/socket.io/topics/merge.js
create mode 100644 src/topics/merge.js
diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js
index f8d333e67a..a7aa7a3ab0 100644
--- a/src/socket.io/topics.js
+++ b/src/socket.io/topics.js
@@ -16,6 +16,7 @@ require('./topics/move')(SocketTopics);
require('./topics/tools')(SocketTopics);
require('./topics/infinitescroll')(SocketTopics);
require('./topics/tags')(SocketTopics);
+require('./topics/merge')(SocketTopics);
SocketTopics.post = function (socket, data, callback) {
if (!data) {
diff --git a/src/socket.io/topics/merge.js b/src/socket.io/topics/merge.js
new file mode 100644
index 0000000000..54275606e6
--- /dev/null
+++ b/src/socket.io/topics/merge.js
@@ -0,0 +1,27 @@
+'use strict';
+
+var async = require('async');
+var topics = require('../../topics');
+var privileges = require('../../privileges');
+
+module.exports = function (SocketTopics) {
+ SocketTopics.merge = function (socket, tids, callback) {
+ if (!Array.isArray(tids)) {
+ return callback(new Error('[[error:invalid-data]]'));
+ }
+
+ async.waterfall([
+ function (next) {
+ async.map(tids, function (tid, next) {
+ privileges.topics.isAdminOrMod(tid, socket.uid, next);
+ }, next);
+ },
+ function (allowed, next) {
+ if (allowed.includes(false)) {
+ return next(new Error('[[error:no-privileges]]'));
+ }
+ topics.merge(tids, next);
+ },
+ ], callback);
+ };
+};
diff --git a/src/topics.js b/src/topics.js
index 5f744316c0..44c263210d 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -31,6 +31,7 @@ require('./topics/suggested')(Topics);
require('./topics/tools')(Topics);
require('./topics/thumb')(Topics);
require('./topics/bookmarks')(Topics);
+require('./topics/merge')(Topics);
Topics.exists = function (tid, callback) {
db.isSortedSetMember('topics:tid', tid, callback);
@@ -252,7 +253,7 @@ function getMainPostAndReplies(topic, set, uid, start, stop, reverse, callback)
return callback(null, []);
}
- if (topic.mainPid && start === 0) {
+ if (parseInt(topic.mainPid, 10) && start === 0) {
pids.unshift(topic.mainPid);
}
posts.getPostsByPids(pids, uid, next);
diff --git a/src/topics/merge.js b/src/topics/merge.js
new file mode 100644
index 0000000000..2c1df0df61
--- /dev/null
+++ b/src/topics/merge.js
@@ -0,0 +1,33 @@
+'use strict';
+
+var async = require('async');
+
+module.exports = function (Topics) {
+ Topics.merge = function (tids, callback) {
+ var mergeIntoTid = findOldestTopic(tids);
+
+ var otherTids = tids.filter(function (tid) {
+ return tid && parseInt(tid, 10) !== parseInt(mergeIntoTid, 10);
+ });
+
+ async.eachSeries(otherTids, function (tid, next) {
+ async.waterfall([
+ function (next) {
+ Topics.getPids(tid, next);
+ },
+ function (pids, next) {
+ async.eachSeries(pids, function (pid, next) {
+ Topics.movePostToTopic(pid, mergeIntoTid, next);
+ }, next);
+ },
+ function (next) {
+ Topics.setTopicField(tid, 'mainPid', 0, next);
+ },
+ ], next);
+ }, callback);
+ };
+
+ function findOldestTopic(tids) {
+ return Math.min.apply(null, tids);
+ }
+};
diff --git a/src/topics/posts.js b/src/topics/posts.js
index ebfbd106c0..9816d8178f 100644
--- a/src/topics/posts.js
+++ b/src/topics/posts.js
@@ -326,7 +326,7 @@ module.exports = function (Topics) {
}, next);
},
function (results, next) {
- if (results.mainPid) {
+ if (parseInt(results.mainPid, 10)) {
results.pids = [results.mainPid].concat(results.pids);
}
next(null, results.pids);
diff --git a/test/topics.js b/test/topics.js
index e6c341cb9e..15b839f996 100644
--- a/test/topics.js
+++ b/test/topics.js
@@ -1699,4 +1699,89 @@ describe('Topic\'s', function () {
});
});
});
+
+ describe('topic merge', function (done) {
+ var uid;
+ var topic1Data;
+ var topic2Data;
+
+ before(function (done) {
+ async.waterfall([
+ function (next) {
+ User.create({ username: 'mergevictim' }, next);
+ },
+ function (_uid, next) {
+ uid = _uid;
+ topics.post({ uid: uid, cid: categoryObj.cid, title: 'topic 1', content: 'topic 1 OP' }, next);
+ },
+ function (result, next) {
+ topic1Data = result.topicData;
+ topics.post({ uid: uid, cid: categoryObj.cid, title: 'topic 2', content: 'topic 2 OP' }, next);
+ },
+ function (result, next) {
+ topic2Data = result.topicData;
+ topics.reply({ uid: uid, content: 'topic 1 reply', tid: topic1Data.tid }, next);
+ },
+ function (postData, next) {
+ topics.reply({ uid: uid, content: 'topic 2 reply', tid: topic2Data.tid }, next);
+ },
+ ], done);
+ });
+
+ it('should error if data is not an array', function (done) {
+ socketTopics.merge({ uid: 0 }, null, function (err) {
+ assert.equal(err.message, '[[error:invalid-data]]');
+ done();
+ });
+ });
+
+ it('should error if user does not have privileges', function (done) {
+ socketTopics.merge({ uid: 0 }, [topic2Data.tid, topic1Data.tid], function (err) {
+ assert.equal(err.message, '[[error:no-privileges]]');
+ done();
+ });
+ });
+
+ it('should merge 2 topics', function (done) {
+ async.waterfall([
+ function (next) {
+ socketTopics.merge({ uid: adminUid }, [topic2Data.tid, topic1Data.tid], next);
+ },
+ function (next) {
+ async.parallel({
+ topic1: function (next) {
+ async.waterfall([
+ function (next) {
+ topics.getTopicData(topic1Data.tid, next);
+ },
+ function (topicData, next) {
+ topics.getTopicWithPosts(topicData, 'tid:' + topicData.tid + ':posts', adminUid, 0, 19, false, next);
+ },
+ ], next);
+ },
+ topic2: function (next) {
+ async.waterfall([
+ function (next) {
+ topics.getTopicData(topic2Data.tid, next);
+ },
+ function (topicData, next) {
+ topics.getTopicWithPosts(topicData, 'tid:' + topicData.tid + ':posts', adminUid, 0, 19, false, next);
+ },
+ ], next);
+ },
+ }, next);
+ },
+ function (results, next) {
+ assert.equal(results.topic1.posts.length, 4);
+ assert.equal(results.topic2.posts.length, 0);
+
+ assert.equal(results.topic1.posts[0].content, 'topic 1 OP');
+ assert.equal(results.topic1.posts[1].content, 'topic 2 OP');
+ assert.equal(results.topic1.posts[2].content, 'topic 1 reply');
+ assert.equal(results.topic1.posts[3].content, 'topic 2 reply');
+ done();
+ },
+ ], done);
+ });
+ });
});
From 78c83f25617d42cd43b2b4f8f16b3dc3456c6049 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Thu, 30 Nov 2017 12:53:36 -0500
Subject: [PATCH 095/178] fixing tests
---
public/src/modules/helpers.js | 2 +-
test/template-helpers.js | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js
index 039eeb3250..07515b9834 100644
--- a/public/src/modules/helpers.js
+++ b/public/src/modules/helpers.js
@@ -212,7 +212,7 @@
if (block.teaser.user.picture) {
return ' ';
}
- return '' + block.teaser.user['icon:text'] + ' ';
+ return '' + block.teaser.user['icon:text'] + ' ';
}
if (block.user.picture) {
return ' ';
diff --git a/test/template-helpers.js b/test/template-helpers.js
index eb145e98a8..8ada13f29e 100644
--- a/test/template-helpers.js
+++ b/test/template-helpers.js
@@ -164,28 +164,28 @@ describe('helpers', function () {
it('should render digest avatar', function (done) {
var block = { teaser: { user: { username: 'baris', picture: '/uploads/1.png' } } };
var html = helpers.renderDigestAvatar(block);
- assert.equal(html, ' ');
+ assert.equal(html, ' ');
done();
});
it('should render digest avatar', function (done) {
var block = { teaser: { user: { username: 'baris', 'icon:text': 'B', 'icon:bgColor': '#ff000' } } };
var html = helpers.renderDigestAvatar(block);
- assert.equal(html, '' + block.teaser.user['icon:text'] + ' ');
+ assert.equal(html, '' + block.teaser.user['icon:text'] + ' ');
done();
});
it('should render digest avatar', function (done) {
var block = { user: { username: 'baris', picture: '/uploads/1.png' } };
var html = helpers.renderDigestAvatar(block);
- assert.equal(html, ' ');
+ assert.equal(html, ' ');
done();
});
it('should render digest avatar', function (done) {
var block = { user: { username: 'baris', 'icon:text': 'B', 'icon:bgColor': '#ff000' } };
var html = helpers.renderDigestAvatar(block);
- assert.equal(html, '' + block.user['icon:text'] + ' ');
+ assert.equal(html, '' + block.user['icon:text'] + ' ');
done();
});
From da5997a06edee46e0607cc3b8548055c90d76b57 Mon Sep 17 00:00:00 2001
From: caoyi
Date: Fri, 1 Dec 2017 02:01:51 +0800
Subject: [PATCH 096/178] Fix typo (#6135)
Fix typo
---
test/mocks/databasemock.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js
index a3642bb78d..96eada54b2 100644
--- a/test/mocks/databasemock.js
+++ b/test/mocks/databasemock.js
@@ -48,7 +48,7 @@ if (!testDbConfig) {
' "host": "127.0.0.1",\n' +
' "port": "27017",\n' +
' "password": "",\n' +
- ' "database": "1\n' +
+ ' "database": "1"\n' +
'}\n' +
' or (mongo) in a replicaset\n' +
'"test_database": {\n' +
From a7a3f3619b070ce000c91321f13eb38e561fdc4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Thu, 30 Nov 2017 14:24:13 -0500
Subject: [PATCH 097/178] dont allow login with invalid ip, escape ip display
on user/info page
---
src/controllers/authentication.js | 39 +++++++++++++++++--------------
src/meta/blacklist.js | 19 +++++++--------
src/user/admin.js | 34 +++++++++++++++++++++------
src/user/auth.js | 16 ++++++++-----
test/authentication.js | 30 ++++++++++++++++++++++++
test/blacklist.js | 20 +++++-----------
6 files changed, 103 insertions(+), 55 deletions(-)
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index 73eb6be287..c547ada0dd 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -6,6 +6,7 @@ var passport = require('passport');
var nconf = require('nconf');
var validator = require('validator');
var _ = require('lodash');
+var ipaddr = require('ipaddr.js');
var db = require('../database');
var meta = require('../meta');
@@ -289,26 +290,30 @@ authenticationController.doLogin = function (req, uid, callback) {
authenticationController.onSuccessfulLogin = function (req, uid, callback) {
var uuid = utils.generateUUID();
- req.session.meta = {};
-
- delete req.session.forceLogin;
-
- // Associate IP used during login with user account
- user.logIP(uid, req.ip);
- req.session.meta.ip = req.ip;
-
- // Associate metadata retrieved via user-agent
- req.session.meta = _.extend(req.session.meta, {
- uuid: uuid,
- datetime: Date.now(),
- platform: req.useragent.platform,
- browser: req.useragent.browser,
- version: req.useragent.version,
- });
async.waterfall([
- async.apply(meta.blacklist.test, req.ip),
function (next) {
+ meta.blacklist.test(req.ip, next);
+ },
+ function (next) {
+ user.logIP(uid, req.ip, next);
+ },
+ function (next) {
+ req.session.meta = {};
+
+ delete req.session.forceLogin;
+ // Associate IP used during login with user account
+ req.session.meta.ip = req.ip;
+
+ // Associate metadata retrieved via user-agent
+ req.session.meta = _.extend(req.session.meta, {
+ uuid: uuid,
+ datetime: Date.now(),
+ platform: req.useragent.platform,
+ browser: req.useragent.browser,
+ version: req.useragent.version,
+ });
+
async.parallel([
function (next) {
user.auth.addSession(uid, req.sessionID, next);
diff --git a/src/meta/blacklist.js b/src/meta/blacklist.js
index c8d5b6a9fb..2b2f897284 100644
--- a/src/meta/blacklist.js
+++ b/src/meta/blacklist.js
@@ -68,7 +68,12 @@ Blacklist.test = function (clientIp, callback) {
// clientIp = '127.0.15.1:3443'; // IPv4 with port strip port to not fail
clientIp = clientIp.split(':').length === 2 ? clientIp.split(':')[0] : clientIp;
- var addr = ipaddr.parse(clientIp);
+ var addr;
+ try {
+ addr = ipaddr.parse(clientIp);
+ } catch (err) {
+ return callback(err);
+ }
if (
Blacklist._rules.ipv4.indexOf(clientIp) === -1 && // not explicitly specified in ipv4 list
@@ -88,11 +93,7 @@ Blacklist.test = function (clientIp, callback) {
analytics.increment('blacklist');
}
- if (typeof callback === 'function') {
- callback(err);
- } else {
- return !!err;
- }
+ callback(err);
});
} else {
var err = new Error('[[error:blacklisted-ip]]');
@@ -100,11 +101,7 @@ Blacklist.test = function (clientIp, callback) {
analytics.increment('blacklist');
- if (typeof callback === 'function') {
- setImmediate(callback, err);
- } else {
- return true;
- }
+ setImmediate(callback, err);
}
};
diff --git a/src/user/admin.js b/src/user/admin.js
index d463523f89..1b273bca48 100644
--- a/src/user/admin.js
+++ b/src/user/admin.js
@@ -2,21 +2,41 @@
'use strict';
var async = require('async');
+var winston = require('winston');
+var validator = require('validator');
+
var db = require('../database');
var plugins = require('../plugins');
-var winston = require('winston');
module.exports = function (User) {
- User.logIP = function (uid, ip) {
+ User.logIP = function (uid, ip, callback) {
var now = Date.now();
- db.sortedSetAdd('uid:' + uid + ':ip', now, ip || 'Unknown');
- if (ip) {
- db.sortedSetAdd('ip:' + ip + ':uid', now, uid);
- }
+ async.waterfall([
+ function (next) {
+ db.sortedSetAdd('uid:' + uid + ':ip', now, ip || 'Unknown', next);
+ },
+ function (next) {
+ if (ip) {
+ db.sortedSetAdd('ip:' + ip + ':uid', now, uid, next);
+ } else {
+ next();
+ }
+ },
+ ], callback);
};
User.getIPs = function (uid, stop, callback) {
- db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, callback);
+ async.waterfall([
+ function (next) {
+ db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, next);
+ },
+ function (ips, next) {
+ ips = ips.map(function (ip) {
+ return validator.escape(String(ip));
+ });
+ next(null, ips);
+ },
+ ], callback);
};
User.getUsersCSV = function (callback) {
diff --git a/src/user/auth.js b/src/user/auth.js
index 1fbc316f18..6d0002939e 100644
--- a/src/user/auth.js
+++ b/src/user/auth.js
@@ -2,6 +2,7 @@
var async = require('async');
var winston = require('winston');
+var validator = require('validator');
var db = require('../database');
var meta = require('../meta');
var events = require('../events');
@@ -126,12 +127,15 @@ module.exports = function (User) {
next(err, sessions);
});
},
- ], function (err, sessions) {
- callback(err, sessions ? sessions.map(function (sessObj) {
- sessObj.meta.datetimeISO = new Date(sessObj.meta.datetime).toISOString();
- return sessObj.meta;
- }) : undefined);
- });
+ function (sessions, next) {
+ sessions = sessions.map(function (sessObj) {
+ sessObj.meta.datetimeISO = new Date(sessObj.meta.datetime).toISOString();
+ sessObj.meta.ip = validator.escape(String(sessObj.meta.ip));
+ return sessObj.meta;
+ });
+ next(null, sessions);
+ },
+ ], callback);
};
User.auth.addSession = function (uid, sessionId, callback) {
diff --git a/test/authentication.js b/test/authentication.js
index bacbcb679a..99512c7af4 100644
--- a/test/authentication.js
+++ b/test/authentication.js
@@ -235,6 +235,36 @@ describe('authentication', function () {
});
});
+ it('should fail to login if ip address if invalid', function (done) {
+ var jar = request.jar();
+ request({
+ url: nconf.get('url') + '/api/config',
+ json: true,
+ jar: jar,
+ }, function (err, response, body) {
+ if (err) {
+ return done(err);
+ }
+
+ request.post(nconf.get('url') + '/login', {
+ form: {
+ username: 'regular',
+ password: 'regularpwd',
+ },
+ json: true,
+ jar: jar,
+ headers: {
+ 'x-csrf-token': body.csrf_token,
+ 'x-forwarded-for': '',
+ },
+ }, function (err, response, body) {
+ assert.ifError(err);
+ assert.equal(response.statusCode, 500);
+ done();
+ });
+ });
+ });
+
it('should fail to login if user does not exist', function (done) {
loginUser('doesnotexist', 'nopassword', function (err, response, body) {
assert.ifError(err);
diff --git a/test/blacklist.js b/test/blacklist.js
index 763c5364f7..9ec1e557d3 100644
--- a/test/blacklist.js
+++ b/test/blacklist.js
@@ -49,32 +49,24 @@ describe('blacklist', function () {
});
});
- it('should pass ip test against blacklist async', function (done) {
+ it('should pass ip test against blacklist', function (done) {
blacklist.test('3.3.3.3', function (err) {
assert.ifError(err);
done();
});
});
- it('should pass ip test against blacklist sync', function (done) {
- assert(!blacklist.test('3.3.3.3'));
- done();
- });
-
- it('should fail ip test against blacklist async', function (done) {
+ it('should fail ip test against blacklist', function (done) {
blacklist.test('1.1.1.1', function (err) {
assert.equal(err.message, '[[error:blacklisted-ip]]');
done();
});
});
- it('should fail ip test against blacklist sync', function (done) {
- assert(blacklist.test('1.1.1.1'));
- done();
- });
-
it('should pass ip test and not crash with ipv6 address', function (done) {
- assert(!blacklist.test('2001:db8:85a3:0:0:8a2e:370:7334'));
- done();
+ blacklist.test('2001:db8:85a3:0:0:8a2e:370:7334', function (err) {
+ assert.ifError(err);
+ done();
+ });
});
});
From b7714179f65ef36e9e7465541a9a46fbcebf48e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Thu, 30 Nov 2017 14:24:57 -0500
Subject: [PATCH 098/178] removed unused dependency
---
src/controllers/authentication.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index c547ada0dd..8c9666e4f4 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -6,7 +6,6 @@ var passport = require('passport');
var nconf = require('nconf');
var validator = require('validator');
var _ = require('lodash');
-var ipaddr = require('ipaddr.js');
var db = require('../database');
var meta = require('../meta');
From c72864888d8819d935d4ba4591f2c287962ef310 Mon Sep 17 00:00:00 2001
From: Linda Badurina
Date: Tue, 7 Nov 2017 15:47:01 -0500
Subject: [PATCH 099/178] ACP "Posts" Changes
- Seperate section for "Post Length" settings
- New section for settings that only apply to new users
- Changed order of some settings under "posting restrctions" and "new user restrictions"
- Uncapitalized words that shouldn't have been capitalized
---
.../language/en-GB/admin/settings/post.json | 14 +-
src/views/admin/settings/post.tpl | 137 +++++++++++++++++-
2 files changed, 138 insertions(+), 13 deletions(-)
diff --git a/public/language/en-GB/admin/settings/post.json b/public/language/en-GB/admin/settings/post.json
index 7cef2f34a0..ed8140a3c7 100644
--- a/public/language/en-GB/admin/settings/post.json
+++ b/public/language/en-GB/admin/settings/post.json
@@ -6,21 +6,25 @@
"sorting.most-votes": "Most Votes",
"sorting.most-posts": "Most Posts",
"sorting.topic-default": "Default Topic Sorting",
+ "length": "Post Length",
"restrictions": "Posting Restrictions",
+ "restrictions-new": "New User Restrictions",
"restrictions.post-queue": "Enable post queue",
+ "restrictions-new.post-queue": "Enable new user restrictions",
"restrictions.post-queue-help": "Enabling post queue will put the posts of new users in a queue for approval.",
- "restrictions.seconds-between": "Seconds between Posts",
- "restrictions.seconds-between-new": "Seconds between Posts for New Users",
+ "restrictions-new.post-queue-help": "Enabling new user restrictions will set restrictions on posts created by new users.",
+ "restrictions.seconds-between": "Seconds between posts",
+ "restrictions.seconds-between-new": "Seconds between posts for new users",
"restrictions.rep-threshold": "Reputation threshold before this restriction is lifted",
"restrictions.seconds-defore-new": "Seconds before new user can post",
- "restrictions.seconds-edit-after": "Number of seconds users are allowed to edit posts after posting. (0 disabled)",
- "restrictions.seconds-delete-after": "Number of seconds users are allowed to delete posts after posting. (0 disabled)",
+ "restrictions.seconds-edit-after": "Number of seconds before users are allowed to edit posts after posting. (0 disabled)",
+ "restrictions.seconds-delete-after": "Number of seconds before users are allowed to delete posts after posting. (0 disabled)",
"restrictions.replies-no-delete": "Number of replies after users are disallowed to delete their own topics. (0 disabled)",
"restrictions.min-title-length": "Minimum Title Length",
"restrictions.max-title-length": "Maximum Title Length",
"restrictions.min-post-length": "Minimum Post Length",
"restrictions.max-post-length": "Maximum Post Length",
- "restrictions.days-until-stale": "Days until Topic is considered stale",
+ "restrictions.days-until-stale": "Days until topic is considered stale",
"restrictions.stale-help": "If a topic is considered \"stale\", then a warning will be shown to users who attempt to reply to that topic.",
"timestamp": "Timestamp",
"timestamp.cut-off": "Date cut-off (in days)",
diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl
index 094ec4b7e7..c8ecd78c82 100644
--- a/src/views/admin/settings/post.tpl
+++ b/src/views/admin/settings/post.tpl
@@ -24,10 +24,127 @@
+
+
+
+ | |