mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-02 03:55:55 +01:00
Post queue (#5878)
* #5876 * add user to post queue table * add time of post into queue add posting restrictions to queue * fix shouldQueueLogic for guests * add test for post-queue route
This commit is contained in:
committed by
GitHub
parent
1864a50826
commit
f161bbf956
8
public/language/en-GB/admin/manage/post-queue.json
Normal file
8
public/language/en-GB/admin/manage/post-queue.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"post-queue": "Post Queue",
|
||||||
|
"description": "There are no posts in the post queue. <br> To enable this feature, go to <a href=\"%1\">Settings → Post → Posting Restrictions</a> and enable <strong>Post Queue</strong>.",
|
||||||
|
"user": "User",
|
||||||
|
"title": "Title",
|
||||||
|
"content": "Content",
|
||||||
|
"posted": "Posted"
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"manage/tags": "Tags",
|
"manage/tags": "Tags",
|
||||||
"manage/users": "Users",
|
"manage/users": "Users",
|
||||||
"manage/registration": "Registration Queue",
|
"manage/registration": "Registration Queue",
|
||||||
|
"manage/post-queue": "Post Queue",
|
||||||
"manage/groups": "Groups",
|
"manage/groups": "Groups",
|
||||||
"manage/ip-blacklist": "IP Blacklist",
|
"manage/ip-blacklist": "IP Blacklist",
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
"sorting.most-votes": "Most Votes",
|
"sorting.most-votes": "Most Votes",
|
||||||
"sorting.topic-default": "Default Topic Sorting",
|
"sorting.topic-default": "Default Topic Sorting",
|
||||||
"restrictions": "Posting Restrictions",
|
"restrictions": "Posting Restrictions",
|
||||||
|
"restrictions.post-queue": "Enable post queue",
|
||||||
|
"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": "Seconds between Posts",
|
||||||
"restrictions.seconds-between-new": "Seconds between Posts for New Users",
|
"restrictions.seconds-between-new": "Seconds between Posts for New Users",
|
||||||
"restrictions.rep-threshold": "Reputation threshold before this restriction is lifted",
|
"restrictions.rep-threshold": "Reputation threshold before this restriction is lifted",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"topic-post": "You have successfully posted.",
|
"topic-post": "You have successfully posted.",
|
||||||
|
"post-queued": "Your post is queued for approval.",
|
||||||
"authentication-successful": "Authentication Successful",
|
"authentication-successful": "Authentication Successful",
|
||||||
"settings-saved": "Settings saved!"
|
"settings-saved": "Settings saved!"
|
||||||
}
|
}
|
||||||
25
public/src/admin/manage/post-queue.js
Normal file
25
public/src/admin/manage/post-queue.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
define('admin/manage/post-queue', function () {
|
||||||
|
var PostQueue = {};
|
||||||
|
|
||||||
|
PostQueue.init = function () {
|
||||||
|
$('.posts-list').on('click', '[data-action]', function () {
|
||||||
|
var parent = $(this).parents('[data-id]');
|
||||||
|
var action = $(this).attr('data-action');
|
||||||
|
var id = parent.attr('data-id');
|
||||||
|
var method = action === 'accept' ? 'posts.accept' : 'posts.reject';
|
||||||
|
|
||||||
|
socket.emit(method, { id: id }, function (err) {
|
||||||
|
if (err) {
|
||||||
|
return app.alertError(err.message);
|
||||||
|
}
|
||||||
|
parent.remove();
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return PostQueue;
|
||||||
|
});
|
||||||
@@ -4,6 +4,7 @@ var adminController = {
|
|||||||
dashboard: require('./admin/dashboard'),
|
dashboard: require('./admin/dashboard'),
|
||||||
categories: require('./admin/categories'),
|
categories: require('./admin/categories'),
|
||||||
tags: require('./admin/tags'),
|
tags: require('./admin/tags'),
|
||||||
|
postQueue: require('./admin/postqueue'),
|
||||||
blacklist: require('./admin/blacklist'),
|
blacklist: require('./admin/blacklist'),
|
||||||
groups: require('./admin/groups'),
|
groups: require('./admin/groups'),
|
||||||
appearance: require('./admin/appearance'),
|
appearance: require('./admin/appearance'),
|
||||||
|
|||||||
66
src/controllers/admin/postqueue.js
Normal file
66
src/controllers/admin/postqueue.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
|
var db = require('../../database');
|
||||||
|
var user = require('../../user');
|
||||||
|
var pagination = require('../../pagination');
|
||||||
|
var utils = require('../../utils');
|
||||||
|
|
||||||
|
var postQueueController = module.exports;
|
||||||
|
|
||||||
|
postQueueController.get = function (req, res, next) {
|
||||||
|
var page = parseInt(req.query.page, 10) || 1;
|
||||||
|
var postsPerPage = 20;
|
||||||
|
var pageCount = 0;
|
||||||
|
|
||||||
|
var start = (page - 1) * postsPerPage;
|
||||||
|
var stop = start + postsPerPage - 1;
|
||||||
|
|
||||||
|
var postData;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
async.parallel({
|
||||||
|
count: function (next) {
|
||||||
|
db.sortedSetCard('post:queue', next);
|
||||||
|
},
|
||||||
|
ids: function (next) {
|
||||||
|
db.getSortedSetRange('post:queue', start, stop, next);
|
||||||
|
},
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
pageCount = Math.ceil(results.count / postsPerPage);
|
||||||
|
|
||||||
|
var keys = results.ids.map(function (id) {
|
||||||
|
return 'post:queue:' + id;
|
||||||
|
});
|
||||||
|
|
||||||
|
db.getObjects(keys, next);
|
||||||
|
},
|
||||||
|
function (data, next) {
|
||||||
|
postData = data;
|
||||||
|
data.forEach(function (data) {
|
||||||
|
data.data = JSON.parse(data.data);
|
||||||
|
data.data.timestampISO = utils.toISOString(data.data.timestamp);
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
var uids = data.map(function (data) {
|
||||||
|
return data && data.uid;
|
||||||
|
});
|
||||||
|
user.getUsersFields(uids, ['username', 'userslug', 'picture'], next);
|
||||||
|
},
|
||||||
|
function (userData) {
|
||||||
|
postData.forEach(function (postData, index) {
|
||||||
|
postData.user = userData[index];
|
||||||
|
});
|
||||||
|
|
||||||
|
res.render('admin/manage/post-queue', {
|
||||||
|
title: '[[pages:post-queue]]',
|
||||||
|
posts: postData,
|
||||||
|
pagination: pagination.create(page, pageCount),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
], next);
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ var async = require('async');
|
|||||||
|
|
||||||
var user = require('../user');
|
var user = require('../user');
|
||||||
var adminBlacklistController = require('./admin/blacklist');
|
var adminBlacklistController = require('./admin/blacklist');
|
||||||
|
var adminPostQueueController = require('./admin/postqueue');
|
||||||
|
|
||||||
var globalModsController = module.exports;
|
var globalModsController = module.exports;
|
||||||
|
|
||||||
@@ -20,3 +21,17 @@ globalModsController.ipBlacklist = function (req, res, next) {
|
|||||||
},
|
},
|
||||||
], next);
|
], next);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
globalModsController.postQueue = function (req, res, next) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
user.isAdminOrGlobalMod(req.uid, next);
|
||||||
|
},
|
||||||
|
function (isAdminOrGlobalMod, next) {
|
||||||
|
if (!isAdminOrGlobalMod) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
adminPostQueueController.get(req, res, next);
|
||||||
|
},
|
||||||
|
], next);
|
||||||
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ require('./posts/recent')(Posts);
|
|||||||
require('./posts/tools')(Posts);
|
require('./posts/tools')(Posts);
|
||||||
require('./posts/votes')(Posts);
|
require('./posts/votes')(Posts);
|
||||||
require('./posts/bookmarks')(Posts);
|
require('./posts/bookmarks')(Posts);
|
||||||
|
require('./posts/queue')(Posts);
|
||||||
|
|
||||||
Posts.exists = function (pid, callback) {
|
Posts.exists = function (pid, callback) {
|
||||||
db.isSortedSetMember('posts:pid', pid, callback);
|
db.isSortedSetMember('posts:pid', pid, callback);
|
||||||
|
|||||||
153
src/posts/queue.js
Normal file
153
src/posts/queue.js
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
|
var db = require('../database');
|
||||||
|
var user = require('../user');
|
||||||
|
var meta = require('../meta');
|
||||||
|
var topics = require('../topics');
|
||||||
|
var privileges = require('../privileges');
|
||||||
|
var socketHelpers = require('../socket.io/helpers');
|
||||||
|
|
||||||
|
module.exports = function (Posts) {
|
||||||
|
Posts.shouldQueue = function (uid, data, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
user.getUserFields(uid, ['reputation', 'postcount'], next);
|
||||||
|
},
|
||||||
|
function (userData, next) {
|
||||||
|
var shouldQueue = parseInt(meta.config.postQueue, 10) === 1 && (!parseInt(uid, 10) || (parseInt(userData.reputation, 10) <= 0 && parseInt(userData.postcount, 10) <= 0));
|
||||||
|
next(null, shouldQueue);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Posts.addToQueue = function (data, callback) {
|
||||||
|
var type = data.title ? 'topic' : 'reply';
|
||||||
|
var id = type + '-' + Date.now();
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
canPost(type, data, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
db.sortedSetAdd('post:queue', Date.now(), id, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
db.setObject('post:queue:' + id, {
|
||||||
|
id: id,
|
||||||
|
uid: data.uid,
|
||||||
|
type: type,
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
user.setUserField(data.uid, 'lastposttime', Date.now(), next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
next(null, {
|
||||||
|
queued: true,
|
||||||
|
message: '[[success:post-queued]]',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function canPost(type, data, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
if (type === 'topic') {
|
||||||
|
next(null, data.cid);
|
||||||
|
} else if (type === 'reply') {
|
||||||
|
topics.getTopicField(data.tid, 'cid', next);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (cid, next) {
|
||||||
|
async.parallel({
|
||||||
|
canPost: function (next) {
|
||||||
|
if (type === 'topic') {
|
||||||
|
privileges.categories.can('topics:create', data.cid, data.uid, next);
|
||||||
|
} else if (type === 'reply') {
|
||||||
|
privileges.categories.can('topics:reply', cid, data.uid, next);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isReadyToPost: function (next) {
|
||||||
|
user.isReadyToPost(data.uid, cid, next);
|
||||||
|
},
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
if (!results.canPost) {
|
||||||
|
return next(new Error('[[error:no-privileges]]'));
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
Posts.removeFromQueue = function (id, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.sortedSetRemove('post:queue', id, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
db.delete('post:queue:' + id, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Posts.submitFromQueue = function (id, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.getObject('post:queue:' + id, next);
|
||||||
|
},
|
||||||
|
function (data, next) {
|
||||||
|
if (!data) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
data.data = JSON.parse(data.data);
|
||||||
|
} catch (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.type === 'topic') {
|
||||||
|
createTopic(data.data, next);
|
||||||
|
} else if (data.type === 'reply') {
|
||||||
|
createReply(data.data, next);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
Posts.removeFromQueue(id, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function createTopic(data, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
topics.post(data, next);
|
||||||
|
},
|
||||||
|
function (result, next) {
|
||||||
|
socketHelpers.notifyNew(data.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createReply(data, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
topics.reply(data, next);
|
||||||
|
},
|
||||||
|
function (postData, next) {
|
||||||
|
var result = {
|
||||||
|
posts: [postData],
|
||||||
|
'reputation:disabled': parseInt(meta.config['reputation:disabled'], 10) === 1,
|
||||||
|
'downvote:disabled': parseInt(meta.config['downvote:disabled'], 10) === 1,
|
||||||
|
};
|
||||||
|
socketHelpers.notifyNew(data.uid, 'newPost', result);
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -56,6 +56,7 @@ function addRoutes(router, middleware, controllers) {
|
|||||||
router.get('/manage/categories/:category_id/analytics', middlewares, controllers.admin.categories.getAnalytics);
|
router.get('/manage/categories/:category_id/analytics', middlewares, controllers.admin.categories.getAnalytics);
|
||||||
|
|
||||||
router.get('/manage/tags', middlewares, controllers.admin.tags.get);
|
router.get('/manage/tags', middlewares, controllers.admin.tags.get);
|
||||||
|
router.get('/manage/post-queue', middlewares, controllers.admin.postQueue.get);
|
||||||
router.get('/manage/ip-blacklist', middlewares, controllers.admin.blacklist.get);
|
router.get('/manage/ip-blacklist', middlewares, controllers.admin.blacklist.get);
|
||||||
|
|
||||||
router.get('/manage/users', middlewares, controllers.admin.users.sortByJoinDate);
|
router.get('/manage/users', middlewares, controllers.admin.users.sortByJoinDate);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ function modRoutes(app, middleware, controllers) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function globalModRoutes(app, middleware, controllers) {
|
function globalModRoutes(app, middleware, controllers) {
|
||||||
|
setupPageRoute(app, '/post-queue', middleware, [], controllers.globalMods.postQueue);
|
||||||
setupPageRoute(app, '/ip-blacklist', middleware, [], controllers.globalMods.ipBlacklist);
|
setupPageRoute(app, '/ip-blacklist', middleware, [], controllers.globalMods.ipBlacklist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,21 @@ SocketPosts.reply = function (socket, data, callback) {
|
|||||||
data.req = websockets.reqFromSocket(socket);
|
data.req = websockets.reqFromSocket(socket);
|
||||||
data.timestamp = Date.now();
|
data.timestamp = Date.now();
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
posts.shouldQueue(socket.uid, data, next);
|
||||||
|
},
|
||||||
|
function (shouldQueue, next) {
|
||||||
|
if (shouldQueue) {
|
||||||
|
posts.addToQueue(data, next);
|
||||||
|
} else {
|
||||||
|
postReply(socket, data, next);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function postReply(socket, data, callback) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
topics.reply(data, next);
|
topics.reply(data, next);
|
||||||
@@ -50,7 +65,7 @@ SocketPosts.reply = function (socket, data, callback) {
|
|||||||
socketHelpers.notifyNew(socket.uid, 'newPost', result);
|
socketHelpers.notifyNew(socket.uid, 'newPost', result);
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
};
|
}
|
||||||
|
|
||||||
SocketPosts.getRawPost = function (socket, pid, callback) {
|
SocketPosts.getRawPost = function (socket, pid, callback) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
@@ -152,3 +167,26 @@ SocketPosts.getReplies = function (socket, pid, callback) {
|
|||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SocketPosts.accept = function (socket, data, callback) {
|
||||||
|
acceptOrReject(posts.submitFromQueue, socket, data, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
SocketPosts.reject = function (socket, data, callback) {
|
||||||
|
acceptOrReject(posts.removeFromQueue, socket, data, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function acceptOrReject(method, socket, data, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
user.isAdminOrGlobalMod(socket.uid, next);
|
||||||
|
},
|
||||||
|
function (isAdminOrGlobalMod, next) {
|
||||||
|
if (!isAdminOrGlobalMod) {
|
||||||
|
return callback(new Error('[[error:no-privileges]]'));
|
||||||
|
}
|
||||||
|
|
||||||
|
method(data.id, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
var topics = require('../topics');
|
var topics = require('../topics');
|
||||||
|
var posts = require('../posts');
|
||||||
var websockets = require('./index');
|
var websockets = require('./index');
|
||||||
var user = require('../user');
|
var user = require('../user');
|
||||||
var apiController = require('../controllers/api');
|
var apiController = require('../controllers/api');
|
||||||
@@ -25,6 +26,21 @@ SocketTopics.post = function (socket, data, callback) {
|
|||||||
data.req = websockets.reqFromSocket(socket);
|
data.req = websockets.reqFromSocket(socket);
|
||||||
data.timestamp = Date.now();
|
data.timestamp = Date.now();
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
posts.shouldQueue(socket.uid, data, next);
|
||||||
|
},
|
||||||
|
function (shouldQueue, next) {
|
||||||
|
if (shouldQueue) {
|
||||||
|
posts.addToQueue(data, next);
|
||||||
|
} else {
|
||||||
|
postTopic(socket, data, next);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function postTopic(socket, data, callback) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
topics.post(data, next);
|
topics.post(data, next);
|
||||||
@@ -38,7 +54,7 @@ SocketTopics.post = function (socket, data, callback) {
|
|||||||
socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
|
socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
};
|
}
|
||||||
|
|
||||||
SocketTopics.postcount = function (socket, tid, callback) {
|
SocketTopics.postcount = function (socket, tid, callback) {
|
||||||
topics.getTopicField(tid, 'postcount', callback);
|
topics.getTopicField(tid, 'postcount', callback);
|
||||||
|
|||||||
59
src/views/admin/manage/post-queue.tpl
Normal file
59
src/views/admin/manage/post-queue.tpl
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="post-queue panel panel-primary">
|
||||||
|
<div class="panel-heading">
|
||||||
|
[[admin/manage/post-queue:post-queue]]
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- IF !posts.length -->
|
||||||
|
<p class="panel-body">
|
||||||
|
[[admin/manage/post-queue:description, {config.relative_path}/admin/settings/post#posting-restrictions]]
|
||||||
|
</p>
|
||||||
|
<!-- ENDIF !posts.length -->
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped posts-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>[[admin/manage/post-queue:user]]</th>
|
||||||
|
<th>[[admin/manage/post-queue:title]]</th>
|
||||||
|
<th>[[admin/manage/post-queue:content]]</th>
|
||||||
|
<th>[[admin/manage/post-queue:posted]]</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- BEGIN posts -->
|
||||||
|
<tr data-id="{posts.id}">
|
||||||
|
<td class="col-md-1">
|
||||||
|
<!-- IF posts.user.userslug -->
|
||||||
|
<a href="/uid/{posts.user.uid}">{posts.user.username}</a>
|
||||||
|
<!-- ELSE -->
|
||||||
|
{posts.user.username}
|
||||||
|
<!-- ENDIF posts.user.userslug -->
|
||||||
|
</td>
|
||||||
|
<td class="col-md-2">
|
||||||
|
{posts.data.title}
|
||||||
|
</td>
|
||||||
|
<td class="col-md-7">
|
||||||
|
{posts.data.content}
|
||||||
|
</td>
|
||||||
|
<td class="col-md-1">
|
||||||
|
<span class="timeago" title={posts.data.timestampISO}></span>
|
||||||
|
</td>
|
||||||
|
<td class="col-md-1">
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button class="btn btn-success btn-xs" data-action="accept"><i class="fa fa-check"></i></button>
|
||||||
|
<button class="btn btn-danger btn-xs" data-action="delete"><i class="fa fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- END posts -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- IMPORT partials/paginator.tpl -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
<li><a href="{relative_path}/admin/manage/groups">[[admin/menu:manage/groups]]</a></li>
|
<li><a href="{relative_path}/admin/manage/groups">[[admin/menu:manage/groups]]</a></li>
|
||||||
<li><a href="{relative_path}/admin/manage/tags">[[admin/menu:manage/tags]]</a></li>
|
<li><a href="{relative_path}/admin/manage/tags">[[admin/menu:manage/tags]]</a></li>
|
||||||
<li><a href="{relative_path}/admin/manage/registration">[[admin/menu:manage/registration]]</a></li>
|
<li><a href="{relative_path}/admin/manage/registration">[[admin/menu:manage/registration]]</a></li>
|
||||||
|
<li><a href="{relative_path}/admin/manage/post-queue">[[admin/menu:manage/post-queue]]</a></li>
|
||||||
<li><a href="{relative_path}/admin/manage/ip-blacklist">[[admin/menu:manage/ip-blacklist]]</a></li>
|
<li><a href="{relative_path}/admin/manage/ip-blacklist">[[admin/menu:manage/ip-blacklist]]</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
@@ -191,6 +192,7 @@
|
|||||||
<li><a href="{relative_path}/admin/manage/groups">[[admin/menu:manage/groups]]</a></li>
|
<li><a href="{relative_path}/admin/manage/groups">[[admin/menu:manage/groups]]</a></li>
|
||||||
<li><a href="{relative_path}/admin/manage/tags">[[admin/menu:manage/tags]]</a></li>
|
<li><a href="{relative_path}/admin/manage/tags">[[admin/menu:manage/tags]]</a></li>
|
||||||
<li><a href="{relative_path}/admin/manage/registration">[[admin/menu:manage/registration]]</a></li>
|
<li><a href="{relative_path}/admin/manage/registration">[[admin/menu:manage/registration]]</a></li>
|
||||||
|
<li><a href="{relative_path}/admin/manage/post-queue">[[admin/menu:manage/post-queue]]</a></li>
|
||||||
<li><a href="{relative_path}/admin/manage/ip-blacklist">[[admin/menu:manage/ip-blacklist]]</a></li>
|
<li><a href="{relative_path}/admin/manage/ip-blacklist">[[admin/menu:manage/ip-blacklist]]</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -28,6 +28,17 @@
|
|||||||
<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/post:restrictions]]</div>
|
<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/post:restrictions]]</div>
|
||||||
<div class="col-sm-10 col-xs-12">
|
<div class="col-sm-10 col-xs-12">
|
||||||
<form>
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||||
|
<input class="mdl-switch__input" type="checkbox" data-field="postQueue">
|
||||||
|
<span class="mdl-switch__label"><strong>[[admin/settings/post:restrictions.post-queue]]</strong></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">
|
||||||
|
[[admin/settings/post:restrictions.post-queue-help]]
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="postDelay">[[admin/settings/post:restrictions.seconds-between]]</label>
|
<label for="postDelay">[[admin/settings/post:restrictions.seconds-between]]</label>
|
||||||
<input id="postDelay" type="text" class="form-control" value="10" data-field="postDelay">
|
<input id="postDelay" type="text" class="form-control" value="10" data-field="postDelay">
|
||||||
|
|||||||
@@ -424,6 +424,31 @@ describe('Admin Controllers', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should load /admin/manage/post-queue', function (done) {
|
||||||
|
request(nconf.get('url') + '/api/admin/manage/post-queue', { jar: jar, json: true }, function (err, res, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(body);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/post-queue should 404 for regular user', function (done) {
|
||||||
|
request(nconf.get('url') + '/api/post-queue', { json: true }, function (err, res, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(body);
|
||||||
|
assert.equal(res.statusCode, 404);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load /post-queue', function (done) {
|
||||||
|
request(nconf.get('url') + '/api/post-queue', { jar: jar, json: true }, function (err, res, body) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert(body);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should load /admin/manage/ip-blacklist', function (done) {
|
it('should load /admin/manage/ip-blacklist', function (done) {
|
||||||
request(nconf.get('url') + '/api/admin/manage/ip-blacklist', { jar: jar, json: true }, function (err, res, body) {
|
request(nconf.get('url') + '/api/admin/manage/ip-blacklist', { jar: jar, json: true }, function (err, res, body) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ var privileges = require('../src/privileges');
|
|||||||
var user = require('../src/user');
|
var user = require('../src/user');
|
||||||
var groups = require('../src/groups');
|
var groups = require('../src/groups');
|
||||||
var socketPosts = require('../src/socket.io/posts');
|
var socketPosts = require('../src/socket.io/posts');
|
||||||
|
var socketTopics = require('../src/socket.io/topics');
|
||||||
|
var meta = require('../src/meta');
|
||||||
|
|
||||||
describe('Post\'s', function () {
|
describe('Post\'s', function () {
|
||||||
var voterUid;
|
var voterUid;
|
||||||
@@ -296,7 +298,6 @@ describe('Post\'s', function () {
|
|||||||
var pid;
|
var pid;
|
||||||
var replyPid;
|
var replyPid;
|
||||||
var tid;
|
var tid;
|
||||||
var meta = require('../src/meta');
|
|
||||||
before(function (done) {
|
before(function (done) {
|
||||||
topics.post({
|
topics.post({
|
||||||
uid: voterUid,
|
uid: voterUid,
|
||||||
@@ -553,7 +554,6 @@ describe('Post\'s', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse signature and remove links and images', function (done) {
|
it('should parse signature and remove links and images', function (done) {
|
||||||
var meta = require('../src/meta');
|
|
||||||
meta.config['signatures:disableLinks'] = 1;
|
meta.config['signatures:disableLinks'] = 1;
|
||||||
meta.config['signatures:disableImages'] = 1;
|
meta.config['signatures:disableImages'] = 1;
|
||||||
var userData = {
|
var userData = {
|
||||||
@@ -749,4 +749,76 @@ describe('Post\'s', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('post queue', function () {
|
||||||
|
var uid;
|
||||||
|
before(function (done) {
|
||||||
|
meta.config.postQueue = 1;
|
||||||
|
user.create({ username: 'newuser' }, function (err, _uid) {
|
||||||
|
assert.ifError(err);
|
||||||
|
uid = _uid;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function (done) {
|
||||||
|
meta.config.postQueue = 0;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add topic to post queue', function (done) {
|
||||||
|
socketTopics.post({ uid: uid }, { title: 'should be queued', content: 'queued topic content', cid: cid }, function (err, result) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(result.queued, true);
|
||||||
|
assert.equal(result.message, '[[success:post-queued]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add reply to post queue', function (done) {
|
||||||
|
socketPosts.reply({ uid: uid }, { content: 'this is a queued reply', tid: topicData.tid }, function (err, result) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(result.queued, true);
|
||||||
|
assert.equal(result.message, '[[success:post-queued]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get queued posts and submit', function (done) {
|
||||||
|
var ids;
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.getSortedSetRange('post:queue', 0, -1, next);
|
||||||
|
},
|
||||||
|
function (_ids, next) {
|
||||||
|
ids = _ids;
|
||||||
|
var keys = ids.map(function (id) {
|
||||||
|
return 'post:queue:' + id;
|
||||||
|
});
|
||||||
|
db.getObjects(keys, next);
|
||||||
|
},
|
||||||
|
function (data, next) {
|
||||||
|
data.forEach(function (data) {
|
||||||
|
data.data = JSON.parse(data.data);
|
||||||
|
});
|
||||||
|
assert.equal(data[0].type, 'topic');
|
||||||
|
assert.equal(data[0].data.content, 'queued topic content');
|
||||||
|
assert.equal(data[1].type, 'reply');
|
||||||
|
assert.equal(data[1].data.content, 'this is a queued reply');
|
||||||
|
|
||||||
|
socketPosts.accept({ uid: globalModUid }, { id: ids[0] }, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
socketPosts.accept({ uid: globalModUid }, { id: ids[1] }, next);
|
||||||
|
},
|
||||||
|
], done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prevent regular users from approving posts', function (done) {
|
||||||
|
socketPosts.accept({ uid: uid }, { id: 1 }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:no-privileges]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user