Mostly functional cross board post moving

merge-requests/341/head
Thomas Lynch 1 year ago
parent 0d6323669f
commit 1191ec4dc8
  1. 53
      controllers/forms/actions.js
  2. 40
      db/posts.js
  3. 64
      models/forms/moveposts.js

@ -1,6 +1,6 @@
'use strict';
const { Posts } = require(__dirname+'/../../db/')
const { Posts, Boards } = require(__dirname+'/../../db/')
, Permissions = require(__dirname+'/../../lib/permission/permissions.js')
, config = require(__dirname+'/../../lib/misc/config.js')
, actionHandler = require(__dirname+'/../../models/forms/actionhandler.js')
@ -44,14 +44,29 @@ module.exports = {
{ result: lengthBody(req.body.ban_reason, 0, globalLimits.fieldLength.ban_reason), expected: false, error: `Ban reason must be ${globalLimits.fieldLength.ban_reason} characters or less` },
{ result: lengthBody(req.body.log_message, 0, globalLimits.fieldLength.log_message), expected: false, error: `Modlog message must be ${globalLimits.fieldLength.log_message} characters or less` },
{ result: (existsBody(req.body.report || req.body.global_report) && lengthBody(req.body.report_reason, 1)), expected: false, blocking: true, error: 'Reports must have a reason' },
{ result: (existsBody(req.body.move) && !req.body.move_to_thread), expected: false, error: 'Must input destinaton thread number to move posts' },
{ result: (existsBody(req.body.move) && (!req.body.move_to_thread && !req.body.move_to_board)), expected: false, error: 'Must input destinaton thread number or board to move posts' },
{ result: async () => {
if (req.body.move && req.body.move_to_thread) {
res.locals.destinationThread = await Posts.threadExists(req.params.board, req.body.move_to_thread);
const moveBoard = req.body.move_to_board || req.params.board;
res.locals.destinationThread = await Posts.threadExists(moveBoard, req.body.move_to_thread);
return res.locals.destinationThread != null;
}
return true;
}, expected: true, error: 'Destination thread for move does not exist' },
}, expected: true, error: 'Destination for move does not exist' },
{ result: async () => {
if (!res.locals.user || !res.locals.user.username) {
return false;
}
if (req.body.move && req.body.move_to_board) {
const destinationBoard = await Boards.findOne(req.body.move_to_board);
if (res.locals.permissions.hasAny(Permissions.MANAGE_GLOBAL_GENERAL, Permissions.MANAGE_BOARD_GENERAL)
|| destinationBoard.staff[res.locals.user.username] != null) {
res.locals.destinationBoard = destinationBoard;
}
return res.locals.destinationBoard != null;
}
return true;
}, expected: true, error: 'Destination for move does not exist, or you do not have permission' },
], res.locals.permissions);
if (errors.length > 0) {
@ -77,25 +92,35 @@ module.exports = {
}
if (req.body.edit) {
//edit post, only allowing one
//edit post only allows single post
//TODO: make this like editnews, a GET endpoint page
return res.render('editpost', {
'post': res.locals.posts[0],
'csrf': req.csrfToken(),
'referer': (req.headers.referer || `/${res.locals.posts[0].board}/manage/thread/${res.locals.posts[0].thread || res.locals.posts[0].postId}.html`) + `#${res.locals.posts[0].postId}`,
});
} else if (req.body.move) {
//TODO: update to fetch destination baord (if req.body.move_to_board), do a perms check for MANAGE_BOARD_GENERAL there, and update V
res.locals.posts = res.locals.posts.filter(p => {
//filter to remove any posts already in the thread (or the OP) of move destination
return p.postId !== req.body.move_to_thread && p.thread !== req.body.move_to_thread;
});
if (res.locals.posts.length === 0) {
return dynamicResponse(req, res, 409, 'message', {
'title': 'Conflict',
'error': 'Destination thread cannot match source thread for move action',
if (!res.locals.destinationBoard && !res.locals.destinationThread) {
return dynamicResponse(req, res, 400, 'message', {
'title': 'Bad Request',
'error': 'Invalid post move destination',
'redirect': `/${req.params.board}/`
});
}
if (req.body.move_to_thread
&& (!req.body.move_to_board || req.body.move_to_board === req.params.board)) {
//If moving to thread on the same board, filter posts that are already in the destination thread
res.locals.posts = res.locals.posts.filter(p => {
return p.postId !== req.body.move_to_thread && p.thread !== req.body.move_to_thread;
});
if (res.locals.posts.length === 0) {
return dynamicResponse(req, res, 409, 'message', {
'title': 'Conflict',
'error': 'Invalid selected posts or destination thread',
'redirect': `/${req.params.board}/`
});
}
}
}
try {

@ -794,13 +794,16 @@ module.exports = {
return db.deleteMany();
},
move: async (postMongoIds, destinationThread, destinationBoard=null) => {
move: async (postMongoIds, crossBoard, destinationThreadId, destinationBoard=null) => {
let bulkWrites = []
, movePostsSet = { thread: destinationThread };
if (destinationBoard) {
, newDestinationThreadId = destinationThreadId;
if (crossBoard) {
//postIds need to be adjusted if moving to a different board
const lastId = await Boards.getNextId(destinationBoard, false, postMongoIds.length);
movePostsSet.board = destinationBoard;
//if moving board and no destination thread, pick the starting ID of the amount we incremented
if (!destinationThreadId) {
newDestinationThreadId = lastId - (postMongoIds.length-1);
}
bulkWrites = postMongoIds.map((postMongoId, index) => ({
'updateOne': {
'filter': {
@ -822,7 +825,10 @@ module.exports = {
}
},
'update': {
'$set': movePostsSet,
'$set': {
'board': destinationBoard,
'thread': newDestinationThreadId,
},
'$unset': {
'replyposts': '',
'replyfiles': '',
@ -835,6 +841,30 @@ module.exports = {
}
}
});
if (!destinationThreadId) {
//No destination thread i.e we are maing a new thread from the selected posts, make one the OP
bulkWrites.push({
'updateOne': {
'filter': {
'_id': postMongoIds[0],
},
'update': {
'$set': {
'thread': null,
//TODO: set these values properly
'replyposts': 0,
'replyfiles': 0,
'sticky': 0,
'locked': 0,
'bumplocked': 0,
'cyclic': 0,
'salt': '',
}
}
}
});
}
//console.log(JSON.stringify(bulkWrites, null, 4));
return db.bulkWrite(bulkWrites);
},

@ -10,14 +10,17 @@ const uploadDirectory = require(__dirname+'/../../lib/file/uploaddirectory.js')
module.exports = async (req, res) => {
const { threads, postIds, postMongoIds } = res.locals.posts.reduce((acc, p) => {
acc.postIds.push(p.postId);
acc.postMongoIds.push(p._id);
if (p.thread === null) {
acc.threads.push(p);
}
return acc;
}, { threads: [], postIds: [], postMongoIds: [] });
const { threads, postIds, postMongoIds } = res.locals.posts
.sort((a, b) => {
return b.date - a.date; //could do postId, doesn't really matter.
}).reduce((acc, p) => {
acc.postIds.push(p.postId);
acc.postMongoIds.push(p._id);
if (p.thread === null) {
acc.threads.push(p);
}
return acc;
}, { threads: [], postIds: [], postMongoIds: [] });
//maybe should filter these? because it will include threads from which child posts are already fetched in the action handler, unlike the deleteposts model
const moveEmits = res.locals.posts.reduce((acc, post) => {
@ -83,30 +86,35 @@ module.exports = async (req, res) => {
});
}
}
//increase file/reply count in thread we are moving the posts to
const { replyposts, replyfiles } = res.locals.posts.reduce((acc, p) => {
acc.replyposts += 1;
acc.replyfiles += p.files.length;
return acc;
}, { replyposts: 0, replyfiles: 0 });
bulkWrites.push({
'updateOne': {
'filter': {
'postId': req.body.move_to_thread,
'board': req.body.move_to_board || req.params.board,
},
'update': {
'$inc': {
'replyposts': replyposts,
'replyfiles': replyfiles,
if (res.locals.destinationThread) {
const { replyposts, replyfiles } = res.locals.posts.reduce((acc, p) => {
acc.replyposts += 1;
acc.replyfiles += p.files.length;
return acc;
}, { replyposts: 0, replyfiles: 0 });
bulkWrites.push({
'updateOne': {
'filter': {
'postId': req.body.move_to_thread,
'board': req.body.move_to_board || req.params.board,
},
'update': {
'$inc': {
'replyposts': replyposts,
'replyfiles': replyfiles,
}
}
}
}
});
});
}
const movedPosts = await Posts.move(postMongoIds, req.body.move_to_thread, req.body.move_to_board).then(result => result.modifiedCount);
const destinationBoard = res.locals.destinationBoard ? res.locals.destinationBoard._id : req.params.board;
const crossBoard = destinationBoard !== req.params.board;
const destinationThreadId = res.locals.destinationThread ? res.locals.destinationThread.postId : (crossBoard ? null : postIds[0]);
const movedPosts = await Posts.move(postMongoIds, crossBoard, destinationThreadId, destinationBoard).then(result => result.modifiedCount);
//emit markPost moves
for (let i = 0; i < moveEmits.length; i++) {

Loading…
Cancel
Save