supporting stuff for modals, scripts, and urlencoded handling in forms.js

merge-requests/208/head
fatchan 4 years ago
parent 80322492d0
commit 5be8431d24
  1. 3
      configs/main.js.example
  2. 6
      db/news.js
  3. 16
      gulp/res/css/style.css
  4. 32
      gulp/res/js/forms.js
  5. 2
      gulp/res/js/modal.js
  6. 2
      gulp/res/js/themelist.js
  7. 5
      gulp/res/js/time.js
  8. 5
      helpers/checks/bancheck.js
  9. 1
      helpers/processip.js
  10. 8
      helpers/tasks.js
  11. 3
      server.js
  12. 4
      views/mixins/modal.pug
  13. 16
      views/pages/home.pug
  14. 2
      views/pages/message.pug
  15. 2
      views/pages/thread.pug

@ -130,6 +130,9 @@ module.exports = {
//how many replies a thread needs to not get removed by early404
early404Replies: 5,
//how many of the most recent newsposts to show on the homepage
maxRecentNews: 3,
/* filter filenames on posts and banners
false=no filtering
true=allow only A-Za-z0-9_-

@ -8,10 +8,12 @@ module.exports = {
db,
find: () => {
find: (limit=0) => {
return db.find({}).sort({
'_id': -1
}).toArray();
})
.limit(limit)
.toArray();
},
insertOne: (news) => {

@ -795,6 +795,18 @@ input:invalid, textarea:invalid {
line-height: 3em;
}
a.button {
-webkit-appearance: button;
-moz-appearance: button;
appearance: button;
text-decoration: none;
color: initial;
}
a.button:hover {
color: initial!important;
}
input[type="button"][disabled] {
opacity: 0.5;
}
@ -1040,6 +1052,10 @@ table, .boardtable {
display: none;
}
table.newstable td:nth-child(2), table.newstable th:nth-child(2) {
display: none;
}
.modal {
top: 5px;
max-height: calc(100% - 10px);

@ -6,7 +6,6 @@ function removeModal() {
function doModal(data) {
const modalHtml = modal({ modal: data });
document.body.insertAdjacentHTML('afterbegin', modalHtml);
new formHandler(document.getElementsByClassName('modal')[0].querySelector('form'));
document.getElementById('modalclose').onclick = removeModal;
document.getElementsByClassName('modal-bg')[0].onclick = removeModal;
}
@ -19,7 +18,7 @@ function isCheckBox(element) {
function formToJSON(form) {
const data = {};
for (element of form.elements) {
if (element.name && element.value && (!isCheckBox(element) || element.checked)) {
if (element.name /*&& element.value*/ && (!isCheckBox(element) || element.checked)) {
if (isCheckBox(element) && data[element.name]) {
if (Array.isArray(data[element.name])) {
data[element.name] = data[element.name].push(element.value);
@ -38,6 +37,7 @@ class formHandler {
constructor(form) {
this.form = form;
this.enctype = this.form.getAttribute('enctype');
this.messageBox = form.querySelector('#message')
this.submit = form.querySelector('input[type="submit"]')
this.originalSubmitText = this.submit.value;
@ -58,8 +58,9 @@ class formHandler {
}
formSubmit(e) {
const xhr = new XMLHttpRequest();
let postData;
if (this.form.getAttribute('enctype') === 'multipart/form-data') {
if (this.enctype === 'multipart/form-data') {
this.fileInput.disabled = true; //palemoon is dumb, so append them instead
postData = new FormData(this.form);
this.fileInput.disabled = false;
@ -70,8 +71,7 @@ class formHandler {
}
}
} else {
xhr.setRequestHeader('Content-Type', 'application/json');
postData = formToJSON(this.form);
postData = new URLSearchParams([...(new FormData(this.form))]);
}
if (this.banned) {
return true;
@ -79,7 +79,6 @@ class formHandler {
e.preventDefault();
}
this.submit.disabled = true;
const xhr = new XMLHttpRequest();
if (this.files && this.files.length > 0) {
//show progress on file uploads
xhr.onloadstart = () => {
@ -100,13 +99,14 @@ class formHandler {
if (xhr.responseText) {
try {
json = JSON.parse(xhr.responseText);
console.log(json)
} catch (e) {
//wasnt json response
}
}
if (xhr.status == 200) {
if (!json) {
if (xhr.responseURL
if (xhr.responseURL
&& xhr.responseURL !== `${location.origin}${this.form.getAttribute('action')}`) {
window.location = xhr.responseURL;
return;
@ -114,7 +114,9 @@ class formHandler {
//todo: show success messages nicely for forms like actions (this doesnt apply to non file forms yet)
}
} else {
if (socket && socket.connected) {
if (json.message || json.messages || json.error || json.errors) {
doModal(json);
} else if (socket && socket.connected) {
window.myPostId = json.postId;
window.location.hash = json.postId
} else {
@ -123,7 +125,6 @@ class formHandler {
}
setLocalStorage('myPostId', json.postId);
forceUpdate();
// window.location.reload();
}
}
this.form.reset();
@ -139,11 +140,10 @@ class formHandler {
if (xhr.status === 413) {
this.clearFiles();
}
//not 200 status, so some error/failed post, wrong captcha, etc
if (json) {
doModal(json);
} else {
//for bans, post form to show TODO: make modal support bans json and send dynamicresponse from it
//for bans, post form to show TODO: make modal support bans json and send dynamicresponse from it (but what about appeals, w/ captcha, etc?)
this.clearFiles(); //dont resubmit files
this.banned = true;
this.form.dispatchEvent(new Event('submit'));
@ -165,6 +165,9 @@ class formHandler {
if (isLive) {
xhr.setRequestHeader('x-using-live', true);
}
if (this.enctype !== 'multipart/form-data') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
xhr.send(postData);
}
@ -200,6 +203,9 @@ class formHandler {
//remove all files from this form
clearFiles() {
if (!this.fileInput) {
return;
}
this.files = []; //empty file list
this.fileInput.value = null; //remove the files for real
if (this.fileRequired) { //reset to required if clearing files
@ -284,9 +290,7 @@ window.addEventListener('settingsReady', () => {
const forms = document.getElementsByTagName('form');
for(let i = 0; i < forms.length; i++) {
if (forms[i].method === 'post'
&& forms[i].encoding === 'multipart/form-data') {
//used only for file posting forms currently.
if (forms[i].method === 'post' /*&& forms[i].encoding === 'multipart/form-data'*/) {
new formHandler(forms[i]);
}
}

@ -55,7 +55,7 @@ pug_html = pug_html + "\u003Cli\u003E" + (pug_escape(null == (pug_interp = error
}
pug_html = pug_html + "\u003C\u002Ful\u003E\u003C\u002Fdiv\u003E";
if (data.link) {
pug_html = pug_html + "\u003Cdiv class=\"row\"\u003E\u003Ca" + (pug_attr("href", data.link.href, true, false)+" target=\"_blank\"") + "\u003E" + (pug_escape(null == (pug_interp = data.link.text) ? "" : pug_interp)) + "\u003C\u002Fa\u003E\u003C\u002Fdiv\u003E";
pug_html = pug_html + "\u003Cdiv class=\"row\"\u003E\u003Ca" + (" class=\"button mv-0\""+pug_attr("href", data.link.href, true, false)+" target=\"_blank\"") + "\u003E" + (pug_escape(null == (pug_interp = data.link.text) ? "" : pug_interp)) + "\u003C\u002Fa\u003E\u003C\u002Fdiv\u003E";
}
}
else

@ -1 +1 @@
const themes = ['choc', 'clear', 'darkblue', 'lain', 'pink', 'tomorrow', 'yotsuba b', 'yotsuba'];const codeThemes = ['a11y-dark', 'a11y-light', 'agate', 'an-old-hope', 'androidstudio', 'arduino-light', 'arta', 'ascetic', 'atelier-cave-dark', 'atelier-cave-light', 'atelier-dune-dark', 'atelier-dune-light', 'atelier-estuary-dark', 'atelier-estuary-light', 'atelier-forest-dark', 'atelier-forest-light', 'atelier-heath-dark', 'atelier-heath-light', 'atelier-lakeside-dark', 'atelier-lakeside-light', 'atelier-plateau-dark', 'atelier-plateau-light', 'atelier-savanna-dark', 'atelier-savanna-light', 'atelier-seaside-dark', 'atelier-seaside-light', 'atelier-sulphurpool-dark', 'atelier-sulphurpool-light', 'atom-one-dark-reasonable', 'atom-one-dark', 'atom-one-light', 'brown-paper', 'brown-papersq', 'codepen-embed', 'color-brewer', 'darcula', 'dark', 'darkula', 'default', 'docco', 'dracula', 'far', 'foundation', 'github-gist', 'github', 'gml', 'googlecode', 'gradient-dark', 'grayscale', 'gruvbox-dark', 'gruvbox-light', 'hopscotch', 'hybrid', 'idea', 'ir-black', 'isbl-editor-dark', 'isbl-editor-light', 'kimbie.dark', 'kimbie.light', 'lightfair', 'magula', 'mono-blue', 'monokai-sublime', 'monokai', 'night-owl', 'nord', 'obsidian', 'ocean', 'paraiso-dark', 'paraiso-light', 'pojoaque', 'pojoaque', 'purebasic', 'qtcreator_dark', 'qtcreator_light', 'railscasts', 'rainbow', 'routeros', 'school-book', 'school-book', 'shades-of-purple', 'solarized-dark', 'solarized-light', 'sunburst', 'tomorrow-night-blue', 'tomorrow-night-bright', 'tomorrow-night-eighties', 'tomorrow-night', 'tomorrow', 'vs', 'vs2015', 'xcode', 'xt256', 'zenburn'];
const themes = ['clear', 'tomorrow', 'lain'];const codeThemes = ['a11y-dark', 'a11y-light', 'agate', 'an-old-hope', 'androidstudio', 'arduino-light', 'arta', 'ascetic', 'atelier-cave-dark', 'atelier-cave-light', 'atelier-dune-dark', 'atelier-dune-light', 'atelier-estuary-dark', 'atelier-estuary-light', 'atelier-forest-dark', 'atelier-forest-light', 'atelier-heath-dark', 'atelier-heath-light', 'atelier-lakeside-dark', 'atelier-lakeside-light', 'atelier-plateau-dark', 'atelier-plateau-light', 'atelier-savanna-dark', 'atelier-savanna-light', 'atelier-seaside-dark', 'atelier-seaside-light', 'atelier-sulphurpool-dark', 'atelier-sulphurpool-light', 'atom-one-dark-reasonable', 'atom-one-dark', 'atom-one-light', 'brown-paper', 'brown-papersq', 'codepen-embed', 'color-brewer', 'darcula', 'dark', 'darkula', 'default', 'docco', 'dracula', 'far', 'foundation', 'github-gist', 'github', 'gml', 'googlecode', 'gradient-dark', 'grayscale', 'gruvbox-dark', 'gruvbox-light', 'hopscotch', 'hybrid', 'idea', 'ir-black', 'isbl-editor-dark', 'isbl-editor-light', 'kimbie.dark', 'kimbie.light', 'lightfair', 'magula', 'mono-blue', 'monokai-sublime', 'monokai', 'night-owl', 'nord', 'obsidian', 'ocean', 'paraiso-dark', 'paraiso-light', 'pojoaque', 'pojoaque', 'purebasic', 'qtcreator_dark', 'qtcreator_light', 'railscasts', 'rainbow', 'routeros', 'school-book', 'school-book', 'shades-of-purple', 'solarized-dark', 'solarized-light', 'sunburst', 'tomorrow-night-blue', 'tomorrow-night-bright', 'tomorrow-night-eighties', 'tomorrow-night', 'tomorrow', 'vs', 'vs2015', 'xcode', 'xt256', 'zenburn'];

@ -52,7 +52,10 @@ const relativeTimeString = (date) => {
}
const changeDateFormat = (date) => {
const options = { hourCycle: hour24 ? 'h23' : 'h12' };
const options = {
hourCycle: hour24 ? 'h23' : 'h12',
hour12: !hour24
};
if (!localTime) {
options.timeZone = SERVER_TIMEZONE;
}

@ -1,7 +1,8 @@
'use strict';
const { Bans } = require(__dirname+'/../../db/')
, hasPerms = require(__dirname+'/hasperms.js');
, hasPerms = require(__dirname+'/hasperms.js')
, dynamicResponse = require(__dirname+'/../dynamic.js');
module.exports = async (req, res, next) => {
@ -14,7 +15,7 @@ module.exports = async (req, res, next) => {
const unseenBans = bans.filter(b => !b.seen).map(b => b._id);
await Bans.markSeen(unseenBans); //mark bans as seen
bans.forEach(ban => ban.seen = true); //mark seen as true in memory for user viewed ban page
return dynamicResponse(req, res, 403, 'message', {
return res.status(403).render('ban', {
bans: bans,
});
}

@ -5,7 +5,6 @@ const { ipHashMode } = require(__dirname+'/../configs/main.js')
, hashIp = require(__dirname+'/haship.js');
module.exports = (req, res, next) => {
const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; //need to consider forwarded-for, etc here and in nginx
const ipVersion = isIP(ip);
if (ipVersion) {

@ -5,7 +5,7 @@ const Mongo = require(__dirname+'/../db/db.js')
, timeUtils = require(__dirname+'/timeutils.js')
, uploadDirectory = require(__dirname+'/files/uploadDirectory.js')
, { remove } = require('fs-extra')
, { debugLogs, pruneModlogs, pruneAfterDays, enableWebring } = require(__dirname+'/../configs/main.js')
, { debugLogs, pruneModlogs, pruneAfterDays, enableWebring, maxRecentNews } = require(__dirname+'/../configs/main.js')
, { Stats, Posts, Files, Boards, News, Modlogs } = require(__dirname+'/../db/')
, render = require(__dirname+'/render.js')
, timeDiffString = require(__dirname+'/timediffstring.js');
@ -195,15 +195,17 @@ module.exports = {
buildHomepage: async () => {
const label = '/index.html';
const start = process.hrtime();
let [ totalStats, boards, fileStats ] = await Promise.all([
let [ totalStats, boards, fileStats, recentNews ] = await Promise.all([
Boards.totalStats(), //overall total posts ever made
Boards.boardSort(0, 20), //top 20 boards sorted by users, pph, total posts
Files.activeContent() //size ans number of files
Files.activeContent(), //size ans number of files
News.find(maxRecentNews), //some recent newsposts
]);
const html = await render('index.html', 'home.pug', {
totalStats,
boards,
fileStats,
recentNews,
});
const end = process.hrtime(start);
debugLogs && console.log(timeDiffString(label, end));

@ -44,7 +44,6 @@ const express = require('express')
app.disable('x-powered-by');
// parse forms
app.use(express.urlencoded({extended: false}));
//app.use(express.json()); //unused atm, will be used with forms.js eventually
// parse cookies
app.use(cookieParser());
@ -105,7 +104,7 @@ const express = require('express')
// catch any unhandled errors
app.use((err, req, res, next) => {
if (err.code === 'EBADCSRFTOKEN') {
return res.status(403).render('message', {
return dynamicResponse(req, res, 403, 'message', {
'title': 'Forbidden',
'message': 'Invalid CSRF token'
});

@ -19,9 +19,7 @@ mixin modal(data)
li #{error}
if data.link
.row
a(href=data.link.href target='_blank') #{data.link.text}
else if data.bans
include ../includes/banform.pug
a.button.mv-0(href=data.link.href target='_blank') #{data.link.text}
else if data.settings
.row
.form-wrapper.flexleft.mt-10

@ -16,6 +16,22 @@ block content
| This is an anonymous imageboard, a type of BBS where anyone can post messages and share images.
| You don't need to register or provide any personal information to make a post.
| Choose a board below to join the discussion, or #[a(href='/create.html') create your own].
if recentNews && recentNews.length > 0
.table-container.flex-center.mv-5
table.newstable
tr
th(colspan=3)
a(href='/news.html') Latest News
each post in recentNews
tr
td
a.left(href=`/news.html#${post._id}`) #{post.title}
td
p.no-m-p #{`${post.message.raw.substring(0,50)}${post.message.raw.length > 50 ? '...' : ''}`}
td
- const newsDate = new Date(post.date);
time.right.reltime(datetime=newsDate.toISOString()) #{newsDate.toLocaleString(undefined, {hour12:false})}
if boards && boards.length > 0
include ../includes/boardtable.pug
each board in boards

@ -21,6 +21,6 @@ block content
li #{error}
if link
div
a(href=link.href target='_blank') #{link.text}
a.button(href=link.href target='_blank') #{link.text}
if redirect
p You will be redirected shortly. If you are not redirected automatically, you can #[a(href=redirect) click here].

@ -6,7 +6,7 @@ include ../mixins/boardheader.pug
block head
script(src='/js/all.js')
- const subjectString = thread.subject || thread.nomarkup ? `${thread.nomarkup.substring(0, globalLimits.fieldLength.subject)}${thread.nomarkup.length > globalLimits.fieldLength.subject ? '...' : ''}` : thread.postId;
- const subjectString = thread.subject || (thread.nomarkup ? `${thread.nomarkup.substring(0, globalLimits.fieldLength.subject)}${thread.nomarkup.length > globalLimits.fieldLength.subject ? '...' : ''}` : thread.postId);
title /#{board._id}/ - #{subjectString}
if !modview
meta(property='og:site_name', value=meta.siteName)

Loading…
Cancel
Save