news composing and editing

This commit is contained in:
Evert Prants 2017-08-29 15:00:36 +03:00
parent 2899f48d95
commit c12ed739c7
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
10 changed files with 194 additions and 46 deletions

View File

@ -78,6 +78,31 @@ const News = {
article = article[0] article = article[0]
return cleanArticle(article) return cleanArticle(article)
},
compose: async (user, body) => {
let article = {
title: body.title,
content: body.content,
tags: body.tags || '',
user_id: user.id,
created_at: new Date()
}
let result = await Models.News.query().insert(article)
result.slug = slugify(result.title)
return result
},
edit: async (id, body) => {
if (!body.content) return {error: 'Content required'}
let patch = {
content: body.content,
updated_at: new Date()
}
let result = await Models.News.query().patchAndFetchById(id, patch)
if (!result) return {error: 'Something went wrong.'}
return {}
} }
} }

View File

@ -288,6 +288,21 @@ router.get('/news/all/', (req, res) => {
res.redirect('/api/news/all/1') res.redirect('/api/news/all/1')
}) })
router.post('/news/edit/:id', wrap(async (req, res, next) => {
if (!req.session.user || req.session.user.privilege < 1) return next()
if (!req.params.id || isNaN(parseInt(req.params.id))) {
return res.status(400).jsonp({error: 'Invalid ID number.'})
}
let id = parseInt(req.params.id)
let result = await News.edit(id, req.body)
if (result.error) {
return res.status(400).jsonp({error: result.error})
}
res.status(204).end()
}))
// Fetch article // Fetch article
router.get('/news/:id', wrap(async (req, res) => { router.get('/news/:id', wrap(async (req, res) => {
if (!req.params.id || isNaN(parseInt(req.params.id))) { if (!req.params.id || isNaN(parseInt(req.params.id))) {
@ -438,4 +453,9 @@ router.use((req, res) => {
res.status(404).jsonp({error: 'Not found'}) res.status(404).jsonp({error: 'Not found'})
}) })
router.use((err, req, res, next) => {
console.error(err)
res.jsonp({error: 'Internal server error.'})
})
module.exports = router module.exports = router

View File

@ -31,6 +31,7 @@ function setSession (req, user) {
display_name: user.display_name, display_name: user.display_name,
email: user.email, email: user.email,
avatar_file: user.avatar_file, avatar_file: user.avatar_file,
privilege: user.nw_privilege,
session_refresh: Date.now() + 1800000 // 30 minutes session_refresh: Date.now() + 1800000 // 30 minutes
} }
} }
@ -65,10 +66,10 @@ router.use(wrap(async (req, res, next) => {
// Update user session // Update user session
let udata = await API.User.get(req.session.user.id) let udata = await API.User.get(req.session.user.id)
setSession(req, udata)
// Update IP address // Update IP address
await API.User.update(udata, {ip_address: req.realIP}) await API.User.update(udata, {ip_address: req.realIP})
setSession(req, udata)
} }
} }
@ -431,7 +432,7 @@ router.post('/register', accountLimiter, wrap(async (req, res, next) => {
} }
// 6th Check: reCAPTCHA (if configuration contains key) // 6th Check: reCAPTCHA (if configuration contains key)
if (config.security.recaptcha && config.security.recaptcha.site_key) { if (config.security && config.security.recaptcha && config.security.recaptcha.site_key) {
if (!req.body['g-recaptcha-response']) return formError(req, res, 'Please complete the reCAPTCHA!') if (!req.body['g-recaptcha-response']) return formError(req, res, 'Please complete the reCAPTCHA!')
try { try {
@ -648,6 +649,39 @@ router.get('/docs/:name', (req, res, next) => {
res.render('document', {doc: doc}) res.render('document', {doc: doc})
}) })
/*
========
NEWS
========
*/
function privileged (req, res, next) {
if (!req.session.user) return res.redirect('/news')
if (req.session.user.privilege < 1) return res.redirect('/news')
next()
}
router.get('/news/writer', privileged, wrap(async (req, res) => {
res.render('news/composer')
}))
router.post('/news/writer', privileged, wrap(async (req, res) => {
if (req.body.csrf !== req.session.csrf) {
return formError(req, res, 'Invalid session! Try reloading the page.')
}
if (!req.body.title || !req.body.content) {
return formError(req, res, 'Required fields missing!')
}
let result = await News.compose(req.session.user, req.body)
if (result.error) {
return formError(req, res, result.error)
}
res.redirect('/news/' + result.id + '-' + result.slug)
}))
// Serve news // Serve news
router.get('/news/:id?-*', wrap(async (req, res) => { router.get('/news/:id?-*', wrap(async (req, res) => {
let id = parseInt(req.params.id) let id = parseInt(req.params.id)
@ -660,8 +694,12 @@ router.get('/news/:id?-*', wrap(async (req, res) => {
return res.status(404).render('article', {article: null}) return res.status(404).render('article', {article: null})
} }
res.header('Cache-Control', 'max-age=' + 24 * 60 * 60 * 1000) // 1 day let editing = false
res.render('article', {article: article}) if (req.query.edit === '1' && req.session.user && req.session.user.privilege > 1) {
editing = true
}
res.render('news/article', {article: article, editing: editing})
})) }))
router.get('/news/', wrap(async (req, res) => { router.get('/news/', wrap(async (req, res) => {
@ -672,7 +710,7 @@ router.get('/news/', wrap(async (req, res) => {
let news = await News.listNews(page) let news = await News.listNews(page)
res.render('news', {news: news}) res.render('news/news', {news: news})
})) }))
// Render partials // Render partials

View File

@ -228,8 +228,8 @@ $(document).ready(function () {
}) })
} }
window.Dialog.openTemplate = function (title, template, data = {}) { window.Dialog.openTemplate = function (title, template, data) {
window.Dialog.open(title, buildTemplateScript(template, data), true) window.Dialog.open(title, buildTemplateScript(template, data || {}), true)
} }
$('#dialog #close').click(function (e) { $('#dialog #close').click(function (e) {

View File

@ -1,16 +1,16 @@
html html
head head
meta(charset="utf8") meta(charset="utf8")
link(rel="stylesheet", type="text/css", href="https://fonts.googleapis.com/css?family=Open+Sans")
link(rel="stylesheet", type="text/css", href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css")
link(rel="stylesheet", type="text/css", href="/style/main.css")
link(rel="stylesheet", type="text/css", href="/style/admin.css")
block links block links
script. link(rel="stylesheet", type="text/css", href="https://fonts.googleapis.com/css?family=Open+Sans")
window.variables = { link(rel="stylesheet", type="text/css", href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css")
server_time: parseInt('#{server_time}') link(rel="stylesheet", type="text/css", href="/style/main.css")
}; link(rel="stylesheet", type="text/css", href="/style/admin.css")
script(src="/script/admin.js") script.
window.variables = {
server_time: parseInt('#{server_time}')
};
script(src="/script/admin.js")
title title
block title block title
|Icy Network - Administration |Icy Network - Administration

View File

@ -1,24 +0,0 @@
extends layout.pug
block title
if article
|Icy Network - News - #{article.title}
else
|Icy Network - News - 404
block body
.document
.content
if !article
span.error No such article
else
.article
.title= article.title
.author Published by
span #{article.author.display_name}
|at
.timestamp #{new Date(article.created_at)}
.content!= article.content
.return
a(href="/news") Back to news directory

View File

@ -3,10 +3,11 @@ html
head head
meta(charset="utf8") meta(charset="utf8")
meta(name="viewport", content="width=device-width, initial-scale=1") meta(name="viewport", content="width=device-width, initial-scale=1")
link(rel="stylesheet", type="text/css", href="/style/main.css") block links
link(rel="stylesheet", type="text/css", href="https://fonts.googleapis.com/css?family=Open+Sans") link(rel="stylesheet", type="text/css", href="/style/main.css")
link(rel="stylesheet", type="text/css", href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css") link(rel="stylesheet", type="text/css", href="//fonts.googleapis.com/css?family=Open+Sans")
script(src="/script/main.js") link(rel="stylesheet", type="text/css", href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css")
script(src="/script/main.js")
title title
block title block title
body body

55
views/news/article.pug Normal file
View File

@ -0,0 +1,55 @@
extends ../layout.pug
block append links
if editing
script(src="//cdnjs.cloudflare.com/ajax/libs/ckeditor/4.7.2/ckeditor.js")
block title
if article
|Icy Network - News - #{article.title}
else
|Icy Network - News - 404
block body
.document
.content
if !article
span.error No such article
else
.article
if user && user.privilege && user.privilege > 0 && !editing
a.button(style="float: right;" href="?edit=1") Edit
.title= article.title
.author Published by
span #{article.author.display_name}
|at
.timestamp #{new Date(article.created_at)}
if editing
.content(contenteditable="true" id="editor1")!= article.content
else
.content!= article.content
if editing
.button(id="done") Done editing
br
script.
CKEDITOR.disableAutoInline = true;
CKEDITOR.inline('editor1');
$('#done').click(function (e) {
let data = CKEDITOR.instances.editor1.getData();
$.post({
url: '/api/news/edit/#{article.id}',
data: {content: data},
success: function () {
window.location.href = '/news/#{article.id}-#{article.slug}'
},
error: function (e) {
if (e.responseJSON && e.responseJSON.error) {
alert(e.responseJSON.error);
}
}
});
});
.return
a(href="/news") Back to the news archive

31
views/news/composer.pug Normal file
View File

@ -0,0 +1,31 @@
extends ../layout.pug
block append links
script(src="//cdnjs.cloudflare.com/ajax/libs/ckeditor/4.7.2/ckeditor.js")
block title
|Icy Network - News - Compose
block body
.document
.content
if message.text
if message.error
.message.error
span #{message.text}
else
.message
span #{message.text}
form(action="", method="post")
input(type="hidden", name="csrf", value=csrf)
label(for="title") Title
input(type="text", name="title", id="title")
label(for="composer1") Content
textarea(name="content" id="composer1")
label(for="tags") Tags
input(type="text", name="tags", id="tags")
input(type="submit", value="Submit")
script.
CKEDITOR.replace('composer1')
a(href="/news") Back to news directory

View File

@ -1,4 +1,4 @@
extends layout.pug extends ../layout.pug
block title block title
|Icy Network - News |Icy Network - News
@ -6,7 +6,9 @@ block title
block body block body
.document .document
.content .content
h1 Icy Network News if user && user.privilege && user.privilege > 0
a.button(style="float: right;" href="/news/writer") New Article
h1 Icy Network News Archive
if news.error if news.error
span.error There are no articles to show. span.error There are no articles to show.
else else