early post moving, next up refactor to move re-markup for styling logic and backlinks out from deletepost into general case to use when moving posts that have quotes in them

merge-requests/208/head
fatchan 5 years ago
parent f950996aa5
commit fabab059de
  1. 10
      controllers/forms/actions.js
  2. 34
      db/posts.js
  3. 1
      helpers/checks/actionchecker.js
  4. 2
      helpers/paramconverter.js
  5. 48
      models/forms/actionhandler.js
  6. 10
      models/forms/deletepost.js
  7. 42
      models/forms/moveposts.js
  8. 5
      views/includes/actionfooter.pug
  9. 1
      views/includes/captcha.pug

@ -62,6 +62,16 @@ module.exports = async (req, res, next) => {
if ((req.body.report || req.body.global_report) && (!req.body.report_reason || req.body.report_reason.length === 0)) {
errors.push('Reports must have a reason');
}
if (req.body.move) {
if (!req.body.move_to_thread) {
errors.push('Must input destinaton thread number to move posts');
} else if (req.body.move_to_thread) {
const destinationThread = await Posts.threadExists(req.params.board, req.body.move_to_thread);
if (!destinationThread) {
errors.push('Destination thread for move does not exist');
}
}
}
if (errors.length > 0) {
return res.status(400).render('message', {

@ -131,7 +131,7 @@ module.exports = {
getThread: async (board, id) => {
// get thread post and potential replies concurrently
const data = await Promise.all([
const [thread, replies] = await Promise.all([
db.findOne({
'postId': id,
'board': board,
@ -148,9 +148,8 @@ module.exports = {
module.exports.getThreadPosts(board, id)
])
// attach the replies to the thread post
const thread = data[0];
if (thread) {
thread.replies = data[1];
if (thread && replies) {
thread.replies = replies;
}
return thread;
},
@ -417,8 +416,33 @@ module.exports = {
return db.deleteMany();
},
move: (ids, dest) => {
console.log(ids, dest)
return db.updateMany({
'_id': {
'$in': ids
}
}, {
'$set': {
'thread': dest
}
});
},
threadExists: (board, thread) => {
return db.findOne({
'postId': thread,
'board': board,
'thread': null,
}, {
'projection': {
'_id': 1
}
});
},
exists: async (req, res, next) => {
const thread = await module.exports.getThread(req.params.board, req.params.id);
const thread = await module.exports.threadExists(req.params.board, req.params.id);
if (!thread) {
return res.status(404).render('404');
}

@ -11,6 +11,7 @@ const actions = [
{name:'bumplock', global:false, auth:3, passwords:false, build:true},
{name:'report', global:false, auth:4, passwords:false, build:false},
{name:'global_report', global:true, auth:4, passwords:false, build:false},
{name:'move', global:false, auth:3, passwords:false, build:true},
{name:'delete_ip_board', global:true, auth:3, passwords:false, build:true},
{name:'delete_ip_global', global:true, auth:1, passwords:false, build:true},
{name:'dismiss', global:false, auth:3, passwords:false, build:false},

@ -5,7 +5,7 @@ const { ObjectId } = require(__dirname+'/../db/db.js')
'checkedreports', 'checkedbans', 'checkedbanners', 'checkedaccounts']) //only these should be arrays, since express bodyparser can output arrays
, trimFields = ['tags', 'uri', 'moderators', 'filters', 'announcement', 'description', 'message',
'name', 'subject', 'email', 'password', 'default_name', 'report_reason', 'ban_reason', 'log_message'] //trim if we dont want filed with whitespace
, numberFields = ['filter_mode', 'captcha_mode', 'tph_trigger', 'tph_trigger_action', 'reply_limit',
, numberFields = ['filter_mode', 'captcha_mode', 'tph_trigger', 'tph_trigger_action', 'reply_limit', 'move_to_thread',
'max_files', 'thread_limit', 'thread', 'min_thread_message_length', 'min_reply_message_length', 'auth_level'] //convert these to numbers before they hit our routes
, banDurationRegex = /^(?<year>[\d]+y)?(?<month>[\d]+m)?(?<week>[\d]+w)?(?<day>[\d]+d)?(?<hour>[\d]+h)?$/
, msTime = require(__dirname+'/mstime.js')

@ -12,6 +12,7 @@ const { Posts, Boards, Modlogs } = require(__dirname+'/../../db/')
, deletePostsFiles = require(__dirname+'/deletepostsfiles.js')
, reportPosts = require(__dirname+'/reportpost.js')
, dismissReports = require(__dirname+'/dismissreport.js')
, movePosts = require(__dirname+'/moveposts.js')
, { remove } = require('fs-extra')
, uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js')
, buildQueue = require(__dirname+'/../../queue.js')
@ -52,18 +53,16 @@ module.exports = async (req, res, next) => {
const post = res.locals.posts[i];
if (!boardThreadMap[post.board]) {
boardThreadMap[post.board] = {
'directThreads': false,
'directThreads': new Set(),
'threads': new Set()
};
}
if (!post.thread) {
//a thread was directly selected on this board, not just posts. so we handle deletes differently
boardThreadMap[post.board].directThreads = true;
boardThreadMap[post.board].directThreads.add(post.postId);
}
const threadId = post.thread || post.postId;
if (!boardThreadMap[post.board].threads.has(threadId)) {
boardThreadMap[post.board].threads.add(threadId);
}
boardThreadMap[post.board].threads.add(threadId);
}
const beforePages = {};
@ -202,6 +201,23 @@ module.exports = async (req, res, next) => {
}
messages.push(message);
}
if (req.body.move) {
if (boardThreadMap[req.params.board].directThreads.size > 0) {
const threadIds = [...boardThreadMap[req.params.board].directThreads];
const fetchMovePosts = await Posts.db.find({
'board': req.params.board,
'thread': {
'$in': threadIds
}
}).toArray();
res.locals.posts = res.locals.posts.concat(fetchMovePosts);
}
const { message, action } = await movePosts(req, res);
if (action) {
modlogActions.push('Moved');
}
messages.push(message);
}
}
if (Object.keys(combinedQuery).length > 0) {
await Posts.db.updateMany({
@ -303,7 +319,8 @@ module.exports = async (req, res, next) => {
for (let i = 0; i < threadBoards.length; i++) {
const threadBoard = threadBoards[i];
//convert this to an array while we are here
boardThreadMap[threadBoard].threads = [...boardThreadMap[threadBoard].threads]
boardThreadMap[threadBoard].threads = [...boardThreadMap[threadBoard].threads];
boardThreadMap[threadBoard].directThreads = [...boardThreadMap[threadBoard].directThreads];
queryOrs.push({
'board': threadBoard,
'postId': {
@ -342,6 +359,15 @@ module.exports = async (req, res, next) => {
const bounds = threadBounds[boardName];
const board = buildBoards[boardName];
//rebuild impacted threads
if (req.body.move) {
buildQueue.push({
'task': 'buildThread',
'options': {
'threadId': req.body.move_to_thread,
'board': board,
}
});
}
for (let j = 0; j < boardThreadMap[boardName].threads.length; j++) {
buildQueue.push({
'task': 'buildThread',
@ -354,10 +380,10 @@ module.exports = async (req, res, next) => {
//refersh any pages affected
const afterPages = Math.ceil((await Posts.getPages(boardName)) / 10);
let catalogRebuild = true;
if (beforePages[boardName] && beforePages[boardName] !== afterPages) {
//amount of pages changed, rebuild all pages and delete any further pages (if pages amount decreased)
if ((beforePages[boardName] && beforePages[boardName] !== afterPages)
|| req.body.move) { //handle moves here since dates would change and not work in old/new page calculations
if (afterPages < beforePages[boardName]) {
//amount of pages decreased
//amount of pages changed, rebuild all pages and delete any further pages (if pages amount decreased)
for (let k = beforePages[boardName]; k > afterPages; k--) {
//deleting html for pages that no longer should exist
parallelPromises.push(remove(`${uploadDirectory}html/${boardName}/${k}.html`));
@ -377,7 +403,7 @@ module.exports = async (req, res, next) => {
const threadPageOldest = await Posts.getThreadPage(boardName, bounds.oldest);
const threadPageNewest = bounds.oldest.postId === bounds.newest.postId ? threadPageOldest : await Posts.getThreadPage(boardName, bounds.newest);
if (req.body.delete || req.body.delete_ip_board || req.body.delete_ip_global) {
if (!boardThreadMap[boardName].directThreads) {
if (boardThreadMap[boardName].directThreads.length === 0) {
//only deleting posts from threads, so thread order wont change, thus we dont delete all pages after
buildQueue.push({
'task': 'buildBoardMultiple',
@ -426,7 +452,7 @@ module.exports = async (req, res, next) => {
'endpage': afterPages,
}
});
if (!boardThreadMap[boardName].directThreads) {
if (boardThreadMap[boardName].directThreads.length === 0) {
catalogRebuild = false;
//these actions dont affect the catalog tile since not on an OP and dont change reply/image counts
}

@ -7,13 +7,7 @@ const uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.
, linkQuotes = require(__dirname+'/../../helpers/posting/quotes.js')
, simpleMarkdown = require(__dirname+'/../../helpers/posting/markdown.js')
, sanitize = require('sanitize-html')
, sanitizeOptions = {
allowedTags: [ 'span', 'a', 'em', 'strong', 'small' ],
allowedAttributes: {
'a': [ 'href', 'class' ],
'span': [ 'class' ]
}
}
, sanitizeOptions = require(__dirname+'/../../helpers/posting/sanitizeoptions.js');
module.exports = async (posts, board, all=false) => {
@ -125,7 +119,7 @@ module.exports = async (posts, board, all=false) => {
//redo the markup
let message = simpleMarkdown(post.nomarkup);
const { quotedMessage, threadQuotes } = await linkQuotes(post.board, post.nomarkup, post.thread);
message = sanitize(quotedMessage, sanitizeOptions);
message = sanitize(quotedMessage, sanitizeOptions.after);
bulkWrites.push({
'updateOne': {
'filter': {

@ -0,0 +1,42 @@
'use strict';
const uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js')
, { remove } = require('fs-extra')
, { Posts } = require(__dirname+'/../../db/')
, linkQuotes = require(__dirname+'/../../helpers/posting/quotes.js')
, simpleMarkdown = require(__dirname+'/../../helpers/posting/markdown.js')
, sanitize = require('sanitize-html')
, sanitizeOptions = require(__dirname+'/../../helpers/posting/sanitizeoptions.js');
module.exports = async (req, res) => {
const threadIds = new Set(res.locals.posts.filter(p => p.thread == null).map(p => p.postId));
if (threadIds.size > 0) {
//threads moved, so their html/json doesnt need to exist anymore
await Promise.all([...threadIds].map(thread => {
remove(`${uploadDirectory}html/${thread.board}/thread/${thread.postId}.html`)
remove(`${uploadDirectory}json/${thread.board}/thread/${thread.postId}.json`)
}));
}
/*
TODO: make posts get remarked up
1. for all posts selected, use their QUOTES and remove BACKLINKS from quoted posts
2. move the posts
3. remarkup/link all the posts
and will need to refactor the code in deletepost model to make it fit for both delete or moving without much duplication
*/
const postMongoIds = res.locals.posts.map(x => x._id);
const movedPosts = await Posts.move(postMongoIds, req.body.move_to_thread).then(result => result.modifiedCount);
const ret = {
message: 'Moved posts',
action: movedPosts > 0,
};
return ret;
}

@ -50,6 +50,11 @@ details.toggle-label
input(type='text', name='ban_duration', placeholder='ban duration e.g. 7d' autocomplete='off')
label
input(type='text', name='log_message', placeholder='modlog message' autocomplete='off')
label
input.post-check(type='checkbox', name='move' value='1')
| Move
label
input(type='number', name='move_to_thread', placeholder='destination thread No.' autocomplete='off')
label
input.post-check(type='checkbox', name='sticky' value='1')
| Toggle Sticky

@ -1,3 +1,4 @@
noscript.no-m-p
iframe.captcha(src='/captcha.html' 'width=200' height='80' scrolling='no')
.jsonly.captcha(style='display:none;')
input.captchafield(type='text' name='captcha' autocomplete='off' placeholder='captcha text' pattern=".{6}" required title='6 characters')

Loading…
Cancel
Save