diff --git a/controllers/forms/actions.js b/controllers/forms/actions.js index 92a82a45..c3ea7edf 100644 --- a/controllers/forms/actions.js +++ b/controllers/forms/actions.js @@ -48,6 +48,9 @@ module.exports = async (req, res, next) => { } //check that actions are valid + if (req.body.edit && req.body.checkedposts.length > 1) { + errors.push('Must select only 1 post for edit action'); + } if (req.body.postpassword && req.body.postpassword.length > globalLimits.fieldLength.postpassword) { errors.push(`Password must be ${globalLimits.fieldLength.postpassword} characters or less`); } @@ -96,14 +99,19 @@ module.exports = async (req, res, next) => { }) } - if (req.body.move) { + if (req.body.edit) { + //edit post, only allowing one + return res.render('editpost', { + 'post': res.locals.posts[0], + }); + } else if (req.body.move) { res.locals.posts = res.locals.posts.filter(p => { //filter to remove any posts already in the thread (or the OP) of move destionation return p.postId !== req.body.move_to_thread && p.thread !== req.body.move_to_thread; }); if (res.locals.posts.length === 0) { - return res.status(404).render('message', { - 'title': 'Not found', + return res.status(409).render('message', { + 'title': 'Conflict', 'error': 'Destionation thread cannot match source thread for move action', 'redirect': `/${req.params.board}/` }); diff --git a/controllers/forms/globalactions.js b/controllers/forms/globalactions.js index 45884964..9da4fb7b 100644 --- a/controllers/forms/globalactions.js +++ b/controllers/forms/globalactions.js @@ -33,6 +33,9 @@ module.exports = async (req, res, next) => { } //check that actions are valid + if (req.body.edit && req.body.globalcheckedposts.length > 1) { + errors.push('Must select only 1 post for edit action'); + } if (req.body.postpassword && req.body.postpassword.length > globalLimits.fieldLength.postpassword) { errors.push(`Password must be ${globalLimits.fieldLength.postpassword} characters or less`); } @@ -66,6 +69,13 @@ module.exports = async (req, res, next) => { }) } + if (req.body.edit) { + //edit post, only allowing one + return res.render('editpost', { + 'post': res.locals.posts[0], + }); + } + try { await actionHandler(req, res, next); } catch (err) { diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index 1a086be1..52cdd1e9 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -114,6 +114,11 @@ pre { max-width: max-content; max-width: -moz-max-content; } +.edit { + background: transparent!important; + border-color: transparent!important; + width: 80px; +} .fw { width: 100%; } @@ -174,7 +179,7 @@ pre { flex-grow: 1; background: var(--darken); height: 1.75em; - cursor: move; + cursor: grab; display: flex; align-items: center; justify-content: center; diff --git a/gulp/res/js/dragable.js b/gulp/res/js/dragable.js index af225cec..aac4dbfd 100644 --- a/gulp/res/js/dragable.js +++ b/gulp/res/js/dragable.js @@ -109,7 +109,7 @@ class Dragable { stopDrag(e) { if (this.draging) { this.draging = false; - this.handle.style.cursor = 'move'; + this.handle.style.cursor = 'grab'; window.removeEventListener('mousemove', e => this.doDrag(e)); window.removeEventListener('touchmove', e => this.doDrag(e)); } diff --git a/gulpfile.js b/gulpfile.js index 3722e80e..ca882c39 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -163,6 +163,7 @@ function custompages() { return gulp.src([`${paths.pug.src}/custompages/*.pug`, `${paths.pug.src}/pages/404.pug`, `${paths.pug.src}/pages/502.pug`]) .pipe(gulppug({ locals: { + globalLimits: configs.globalLimits, codeLanguages: configs.highlightOptions.languageSubset, defaultTheme: configs.boardDefaults.theme, defaultCodeTheme: configs.boardDefaults.codeTheme, diff --git a/helpers/checks/actionchecker.js b/helpers/checks/actionchecker.js index 3c37421b..1b2423e0 100644 --- a/helpers/checks/actionchecker.js +++ b/helpers/checks/actionchecker.js @@ -4,6 +4,7 @@ const actions = [ {name:'unlink_file', global:true, auth:4, passwords:true, build:true}, {name:'delete_file', global:true, auth:1, passwords:false, build:true}, {name:'spoiler', global:true, auth:4, passwords:true, build:true}, + {name:'edit', global:true, auth:3, passwords:false, build:true}, {name:'delete', global:true, auth:4, passwords:true, build:true}, {name:'lock', global:false, auth:3, passwords:false, build:true}, {name:'sticky', global:false, auth:3, passwords:false, build:true}, diff --git a/views/includes/actionfooter.pug b/views/includes/actionfooter.pug index 0ac41718..0a984191 100644 --- a/views/includes/actionfooter.pug +++ b/views/includes/actionfooter.pug @@ -24,6 +24,9 @@ details.toggle-label#actionform details.actions summary.bold Staff Actions: div + label + input.post-check(type='checkbox', name='edit' value='1') + | Edit Post label input.post-check(type='checkbox', name='delete_ip_thread' value='1') | Delete from IP in thread diff --git a/views/includes/actionfooter_globalmanage.pug b/views/includes/actionfooter_globalmanage.pug index 1feb371d..42154573 100644 --- a/views/includes/actionfooter_globalmanage.pug +++ b/views/includes/actionfooter_globalmanage.pug @@ -11,6 +11,9 @@ details.toggle-label#actionform label input.post-check(type='checkbox', name='spoiler' value='1') | Spoiler Files + label + input.post-check(type='checkbox', name='edit' value='1') + | Edit Post label input.post-check(type='checkbox', name='delete_ip_global' value='1') | Delete from IP globally diff --git a/views/includes/actionfooter_manage.pug b/views/includes/actionfooter_manage.pug index 5ae8c76e..74e6be50 100644 --- a/views/includes/actionfooter_manage.pug +++ b/views/includes/actionfooter_manage.pug @@ -11,6 +11,9 @@ details.toggle-label#actionform label input.post-check(type='checkbox', name='spoiler' value='1') | Spoiler Files + label + input.post-check(type='checkbox', name='edit' value='1') + | Edit Post label input.post-check(type='checkbox', name='global_report' value='1') | Global Report diff --git a/views/pages/editpost.pug b/views/pages/editpost.pug new file mode 100644 index 00000000..08514f39 --- /dev/null +++ b/views/pages/editpost.pug @@ -0,0 +1,51 @@ +extends ../layout.pug + +block head + title Edit Post + +block content + h1.board-title Edit Post + .form-wrapper.flex-center.mv-10 + form.form-post(action='/forms/edit' method='POST') + .anchor(id=post.postId) + div(class=`mv-5 post-container ${post.thread || ban === true ? '' : 'op'}` data-board=post.board data-post-id=post.postId data-user-id=post.userId) + - const postURL = `/${post.board}/thread/${post.thread || post.postId}.html`; + .post-info + span.noselect + label + if !post.thread + include ../includes/posticons.pug + input.edit.post-subject(value=post.subject placeholder='subject' type='text' name='subject' maxlength=globalLimits.fieldLength.subject) + input.edit.post-name(value=post.email type='text' name='email' placeholder='email' maxlength=globalLimits.fieldLength.email) + input.edit.post-name(type='text' name='name' placeholder='name' maxlength=globalLimits.fieldLength.name) + if post.country && post.country.code + span(class=`flag flag-${post.country.code.toLowerCase()}` title=post.country.name alt=post.country.name) + | + if post.tripcode + span.post-tripcode #{post.tripcode} + | + if post.capcode + span.post-capcode #{post.capcode} + | + - const postDate = new Date(post.date); + time.post-date.reltime(datetime=postDate.toISOString()) #{postDate.toLocaleString(undefined, { hour12:false })} + | + if post.userId + span.user-id(style=`background-color: #${post.userId}`) #{post.userId} + | + span.post-links + a.noselect.no-decoration(href=`${postURL}#${post.postId}`) No. + span.post-quoters + a.no-decoration(href=`${postURL}#postform`) #{post.postId} + if !post.thread + | + span.noselect: a(href=`${postURL}#postform`) [Reply] + .post-data + pre.post-message + textarea.edit.fw(name='message' rows='15' placeholder='message') #{post.nomarkup} + if post.banmessage + p.ban + span.message USER WAS BANNED FOR THIS POST + | + span.reason(data-reason=post.banmessage) #{post.banmessage} + input(type='submit', value='save')