board management section and post reports

merge-requests/208/head
fatchan 5 years ago
parent b42a7eafdf
commit f4f5d2c34f
  1. 25
      controllers/forms.js
  2. 60
      db-models/posts.js
  3. 34
      models/forms/edit-post.js
  4. 1
      models/forms/make-post.js
  5. 22
      models/forms/report-post.js
  6. 15
      models/pages/manage.js
  7. 13
      static/css/style.css
  8. 5
      views/includes/deletefooter.pug
  9. 2
      views/includes/navbar.pug
  10. 9
      views/mixins/post.pug
  11. 8
      views/pages/board.pug
  12. 17
      views/pages/manage.pug
  13. 2
      views/pages/message.pug
  14. 8
      views/pages/thread.pug
  15. 18
      wipe.js

@ -7,6 +7,7 @@ const express = require('express')
, Trips = require(__dirname+'/../db-models/trips.js') , Trips = require(__dirname+'/../db-models/trips.js')
, makePost = require(__dirname+'/../models/forms/make-post.js') , makePost = require(__dirname+'/../models/forms/make-post.js')
, deletePost = require(__dirname+'/../models/forms/delete-post.js') , deletePost = require(__dirname+'/../models/forms/delete-post.js')
, reportPost = require(__dirname+'/../models/forms/report-post.js')
, loginAccount = require(__dirname+'/../models/forms/login.js') , loginAccount = require(__dirname+'/../models/forms/login.js')
, registerAccount = require(__dirname+'/../models/forms/register.js') , registerAccount = require(__dirname+'/../models/forms/register.js')
, numberConverter = require(__dirname+'/../helpers/number-converter.js'); , numberConverter = require(__dirname+'/../helpers/number-converter.js');
@ -132,16 +133,22 @@ router.post('/board/:board', Boards.exists, numberConverter, (req, res, next) =>
}); });
// delete post(s) //report, delete, sticky, etc
router.post('/board/:board/delete', Boards.exists, numberConverter, (req, res, next) => { router.post('/board/:board/posts', Boards.exists, numberConverter, (req, res, next) => {
const errors = []; const errors = [];
if (req.body.password && req.body.password.length > 50) { if (req.body.password && req.body.password.length > 50) {
errors.push('Password must be 50 characters or less') errors.push('Password must be 50 characters or less');
}
if (req.body.report && req.body.report.length > 50) {
errors.push('Report must be 50 characters or less');
}
if (req.body.password && req.body.report) {
errors.push('Can only report or delete, not both');
} }
if (!req.body.checked || req.body.checked.length === 0 || req.body.checked.length > 10) { //10 for now just for _some_ limit if (!req.body.checked || req.body.checked.length === 0 || req.body.checked.length > 10) { //10 for now just for _some_ limit
errors.push('Must check 1-10 boxes for posts to delete') errors.push('Must select 1-10 posts')
} }
if (errors.length > 0) { if (errors.length > 0) {
@ -152,7 +159,15 @@ router.post('/board/:board/delete', Boards.exists, numberConverter, (req, res, n
}) })
} }
deletePost(req, res); //we checked to make sure there are not both, so...
if (req.body.report) {
//if theres a report reason, handle reports
reportPost(req, res);
} else {
//otherwise, must be delete request which
//for authed users DOES NOT requoie passwrd
deletePost(req, res);
}
}); });

@ -17,7 +17,8 @@ module.exports = {
},{ },{
'projection': { 'projection': {
'salt': 0, 'salt': 0,
'password': 0 'password': 0,
'reports': 0
} }
}).sort({ }).sort({
'bumped': -1 'bumped': -1
@ -31,6 +32,7 @@ module.exports = {
'projection': { 'projection': {
'salt': 0, 'salt': 0,
'password': 0, 'password': 0,
'reports': 0
} }
}).sort({ }).sort({
'_id': -1 '_id': -1
@ -42,7 +44,7 @@ module.exports = {
}, },
getPages: async (board) => { getPages: (board) => {
return db.collection(board).estimatedDocumentCount(); return db.collection(board).estimatedDocumentCount();
}, },
@ -55,7 +57,8 @@ module.exports = {
}, { }, {
'projection': { 'projection': {
'salt': 0, 'salt': 0,
'password': 0 'password': 0,
'reports': 0
} }
}), }),
module.exports.getThreadPosts(board, id) module.exports.getThreadPosts(board, id)
@ -71,7 +74,7 @@ module.exports = {
}, },
getThreadPosts: async(board, id) => { getThreadPosts: (board, id) => {
// all posts within a thread // all posts within a thread
return db.collection(board).find({ return db.collection(board).find({
@ -79,7 +82,8 @@ module.exports = {
}, { }, {
'projection': { 'projection': {
'salt': 0 , 'salt': 0 ,
'password': 0 'password': 0,
'reports': 0
} }
}).sort({ }).sort({
'_id': 1 '_id': 1
@ -87,7 +91,7 @@ module.exports = {
}, },
getCatalog: async (board) => { getCatalog: (board) => {
// get all threads for catalog // get all threads for catalog
return db.collection(board).find({ return db.collection(board).find({
@ -95,13 +99,14 @@ module.exports = {
}, { }, {
'projection': { 'projection': {
'salt': 0, 'salt': 0,
'password': 0 'password': 0,
'reports': 0
} }
}).toArray(); }).toArray();
}, },
getPost: async (board, id, admin) => { getPost: (board, id, admin) => {
// get a post // get a post
if (admin) { if (admin) {
@ -115,14 +120,15 @@ module.exports = {
}, { }, {
'projection': { 'projection': {
'salt': 0, 'salt': 0,
'password': 0 'password': 0,
'reports': 0
} }
}); });
}, },
//takes array "ids" of post ids //takes array "ids" of post ids
getPosts: async(board, ids, admin) => { getPosts: (board, ids, admin) => {
if (admin) { if (admin) {
return db.collection(board).find({ return db.collection(board).find({
@ -139,7 +145,8 @@ module.exports = {
}, { }, {
'projection': { 'projection': {
'salt': 0, 'salt': 0,
'password': 0 'password': 0,
'reports': 0
} }
}).toArray(); }).toArray();
@ -169,11 +176,36 @@ module.exports = {
}, },
deleteOne: async (board, options) => { reportMany: (board, ids, report) => {
return db.collection(board).updateMany({
'_id': {
'$in': ids
}
}, {
'$push': {
'reports': report
}
});
},
getReports: (board) => {
return db.collection(board).find({
'reports.0': {
'$exists': true
}
}, {
'projection': {
'salt': 0,
'password': 0,
}
}).toArray();
},
deleteOne: (board, options) => {
return db.collection(board).deleteOne(options); return db.collection(board).deleteOne(options);
}, },
deleteMany: async (board, ids) => { deleteMany: (board, ids) => {
return db.collection(board).deleteMany({ return db.collection(board).deleteMany({
'_id': { '_id': {
@ -183,7 +215,7 @@ module.exports = {
}, },
deleteAll: async (board) => { deleteAll: (board) => {
return db.collection(board).deleteMany({}); return db.collection(board).deleteMany({});
}, },

@ -0,0 +1,34 @@
'use strict';
const uuidv4 = require('uuid/v4')
, path = require('path')
, Posts = require(__dirname+'/../../db-models/posts.js')
module.exports = async (req, res, numFiles) => {
// get the post that we are trying to edit
let post;
try {
post = await Posts.getPost(req.params.board, req.body.id, true);
} catch (err) {
console.error(err);
return res.status(500).render('error');
}
if (!thread || thread.thread != null) {
return res.status(400).render('message', {
'title': 'Bad request',
'message': 'Post does not exist.',
'redirect': redirect
});
}
// sticky, lock, sage, spoiler, etc
for (let i = 0; i < req.body.actions.length; i++) {
}
const post = await Posts.updateOne(req.params.board, data)
const successRedirect = `/${req.params.board}/thread/${req.body.thread || post.insertedId}`;
return res.redirect(successRedirect);
}

@ -164,6 +164,7 @@ module.exports = async (req, res, numFiles) => {
'userId': userId, 'userId': userId,
'files': files, 'files': files,
'salt': salt, 'salt': salt,
'reports': []
}; };
const post = await Posts.insertOne(req.params.board, data) const post = await Posts.insertOne(req.params.board, data)

@ -0,0 +1,22 @@
'use strict';
const Posts = require(__dirname+'/../../db-models/posts.js');
module.exports = async (req, res) => {
try {
//push the report to all checked posts
await Posts.reportMany(req.params.board, req.body.checked, req.body.report);
} catch (err) {
console.error(err);
return res.status(500).render('error');
}
//hooray!
return res.render('message', {
'title': 'Success',
'message': `Reported post(s) successfully`,
'redirect': `/${req.params.board}`
});
}

@ -1,10 +1,21 @@
'use strict'; 'use strict';
module.exports = (req, res) => { const Posts = require(__dirname+'/../../db-models/posts.js');
module.exports = async (req, res) => {
let posts;
try {
posts = await Posts.getReports(req.params.board);
} catch (err) {
console.error(err);
return res.status(500).render('error');
}
//render the page //render the page
res.render('manage', { res.render('manage', {
csrf: req.csrfToken() csrf: req.csrfToken(),
posts: posts
}); });
} }

@ -9,6 +9,13 @@ body {
margin: 0; margin: 0;
} }
.reports {
background: #fca!important;
border-color: #c97!important;
border-width: 1px 0;
border-style: solid none;
}
.redtext { .redtext {
color: maroon; color: maroon;
} }
@ -49,7 +56,7 @@ input, textarea {
.delete-wrapper { .delete-wrapper {
align-items: center; align-items: center;
flex-direction: row; /*flex-direction: row;*/
} }
.form-post { .form-post {
@ -129,7 +136,7 @@ input textarea {
max-width: 100%; max-width: 100%;
} }
.post-container:target { .post-container:target, .op:target {
outline: 1px dashed blue; outline: 1px dashed blue;
outline-offset: -1px; outline-offset: -1px;
} }
@ -214,6 +221,8 @@ th, td {
hr { hr {
color: lightgray; color: lightgray;
/*border-top: 1px solid black;
background: lightgray;*/
} }
@media only screen and (max-width: 800px) { @media only screen and (max-width: 800px) {

@ -0,0 +1,5 @@
section.delete-wrapper
p Report OR delete selected posts
input#report(type='report', name='report', placeholder='report reason' autocomplete='off')
input#password(type='password', name='password', placeholder='password (for deletion)' autocomplete='off')
input(type='submit', value='submit')

@ -1,3 +1,5 @@
nav.navbar nav.navbar
a.nav-item(href='/') Home a.nav-item(href='/') Home
a.nav-item(href='/login') Login a.nav-item(href='/login') Login
if board
a.nav-item(href=`/${board._id}/manage`) Manage Board

@ -7,10 +7,7 @@ mixin post(board, post, truncate)
span.post-name #{post.name} span.post-name #{post.name}
span #{post.date.toLocaleString()} span #{post.date.toLocaleString()}
span.user-id(style=`background: #${post.userId}`) #{post.userId} span.user-id(style=`background: #${post.userId}`) #{post.userId}
if post.thread == null span: a(href=`/${board._id}/thread/${post.thread ? post.thread : post._id}#${post._id}`) ##{post._id}
span: a(href=`/${board._id}/thread/${post._id}`) ##{post._id}
else
span: a(href=`/${board._id}/thread/${post.thread}#${post._id}`) ##{post._id}
if post.files.length > 0 if post.files.length > 0
.post-files .post-files
each file in post.files each file in post.files
@ -40,4 +37,6 @@ mixin post(board, post, truncate)
blockquote.post-message !{post.message} blockquote.post-message !{post.message}
else else
blockquote.post-message !{post.message} blockquote.post-message !{post.message}
if post.reports
each report in post.reports
span.reports.post-container #{report}

@ -9,13 +9,13 @@ block content
h4.board-description #{board.description} h4.board-description #{board.description}
include ../includes/postform.pug include ../includes/postform.pug
hr(size=1) hr(size=1)
form(action='/forms/board/'+board._id+'/delete' method='POST' enctype='application/x-www-form-urlencoded') form(action='/forms/board/'+board._id+'/posts' method='POST' enctype='application/x-www-form-urlencoded')
input(type='hidden' name='_csrf' value=csrf) input(type='hidden' name='_csrf' value=csrf)
if threads.length === 0 if threads.length === 0
p No posts. p No posts.
hr(size=1) hr(size=1)
for thread in threads for thread in threads
section.thread(id=thread._id) section.thread
+post(board, thread, true) +post(board, thread, true)
for post in thread.replies for post in thread.replies
+post(board, post, true) +post(board, post, true)
@ -25,6 +25,4 @@ block content
- for(let i = 0; i < pages; i++) - for(let i = 0; i < pages; i++)
span: a(href=`/${board._id}/${i+1}`) #{i+1} span: a(href=`/${board._id}/${i+1}`) #{i+1}
hr(size=1) hr(size=1)
section.delete-wrapper include ../includes/deletefooter.pug
input#password(type='password', name='password', placeholder='password (for deletion)' autocomplete='off')
input(type='submit', value='delete')

@ -1,7 +1,22 @@
extends ../layout.pug extends ../layout.pug
include ../mixins/post.pug
block head block head
title Login title Login
block content block content
p dummy manage page h1.board-title /#{board._id}/ - #{board.name}
h4.board-description Management Panel
hr(size=1)
form(action='/forms/board/'+board._id+'/posts' method='POST' enctype='application/x-www-form-urlencoded')
input(type='hidden' name='_csrf' value=csrf)
if posts.length === 0
p No posts.
hr(size=1)
for post in posts
section.thread
+post(board, post)
hr(size=1)
section.delete-wrapper
input(type='submit', value='delete')

@ -1,7 +1,7 @@
extends ../layout.pug extends ../layout.pug
block head block head
meta(http-equiv="refresh" content=`6;url=${redirect}`) meta(http-equiv="refresh" content=`3;url=${redirect}`)
block content block content
h1 #{title} h1 #{title}

@ -14,16 +14,14 @@ block content
hr(size=1) hr(size=1)
include ../includes/postform.pug include ../includes/postform.pug
hr(size=1) hr(size=1)
form(action='/forms/board/'+board._id+'/delete' method='POST' enctype='application/x-www-form-urlencoded') form(action='/forms/board/'+board._id+'/posts' method='POST' enctype='application/x-www-form-urlencoded')
input(type='hidden' name='_csrf' value=csrf) input(type='hidden' name='_csrf' value=csrf)
section.thread(id=thread._id) section.thread
+post(board, thread) +post(board, thread)
for post in thread.replies for post in thread.replies
+post(board, post) +post(board, post)
hr(size=1) hr(size=1)
section.delete-wrapper include ../includes/deletefooter.pug
input#password(type='password', name='password', placeholder='password (for deletion)' autocomplete='off')
input(type='submit', value='delete')

@ -44,6 +44,24 @@ const Mongo = require(__dirname+'/helpers/db.js')
await Posts.db.collection('b').createIndex({"bumped": 1}); await Posts.db.collection('b').createIndex({"bumped": 1});
await Posts.db.collection('pol').createIndex({"thread": 1}); await Posts.db.collection('pol').createIndex({"thread": 1});
await Posts.db.collection('pol').createIndex({"bumped": 1}); await Posts.db.collection('pol').createIndex({"bumped": 1});
await Posts.db.collection('pol').createIndex({
'reports.0': 1
}, {
partialFilterExpression: {
'reports.0': {
'$exists': true
}
}
});
await Posts.db.collection('b').createIndex({
'reports.0': 1
}, {
partialFilterExpression: {
'reports.0': {
'$exists': true
}
}
});
await readdir('static/img/').then(async files => { await readdir('static/img/').then(async files => {
await Promise.all(files.map(async file => { await Promise.all(files.map(async file => {
unlink(path.join('static/img/', file)); unlink(path.join('static/img/', file));

Loading…
Cancel
Save