Added article by support
This commit is contained in:
parent
48e6d56ec1
commit
074ef0fbdb
18 changed files with 119 additions and 26 deletions
|
@ -136,7 +136,7 @@ const Article = bookshelf.createModel({
|
|||
.fetchPage({
|
||||
pageSize: 10,
|
||||
page: page,
|
||||
withRelated: ['files', 'media', 'banner', 'parent'],
|
||||
withRelated: ['files', 'media', 'banner', 'parent', 'staff'],
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
@ -55,6 +55,10 @@ export default class ArticleRoutes {
|
|||
async createArticle(ctx) {
|
||||
await this.security.validUpdate(ctx)
|
||||
|
||||
if (!ctx.request.body.staff_id) {
|
||||
ctx.request.body.staff_id = ctx.state.user.id
|
||||
}
|
||||
|
||||
ctx.body = await this.Article.create(ctx.request.body)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ const requiredFields = [
|
|||
const validFields = [
|
||||
'name',
|
||||
'path',
|
||||
'staff_id',
|
||||
'description',
|
||||
'parent_id',
|
||||
'media_id',
|
||||
|
|
|
@ -28,6 +28,6 @@ export default class AuthHelper {
|
|||
throw err
|
||||
}
|
||||
|
||||
return this.jwt.createToken(staff.get('email'), staff.get('level'))
|
||||
return this.jwt.createToken(staff.id, staff.get('email'), staff.get('level'))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export default class AuthRoutes {
|
|||
level = staff.get('level')
|
||||
}
|
||||
|
||||
ctx.body = { token: this.jwt.createToken(output.email, level) }
|
||||
ctx.body = { token: this.jwt.createToken(staff.id, output.email, level) }
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -43,8 +43,9 @@ export default class Jwt {
|
|||
return this.jwt.decode(token)
|
||||
}
|
||||
|
||||
createToken(email, level, opts) {
|
||||
createToken(id, email, level, opts) {
|
||||
return this.sign({
|
||||
id: id,
|
||||
email: email,
|
||||
level: level,
|
||||
}, email, opts)
|
||||
|
|
|
@ -17,6 +17,10 @@ function mapArticle(trim = false, x, includeBanner = false, includeFiles = true)
|
|||
path: x.path,
|
||||
description: x.description,
|
||||
name: x.name,
|
||||
staff: x.staff && ({
|
||||
id: x.staff.id,
|
||||
fullname: x.staff.fullname,
|
||||
}) || null,
|
||||
media: x.media && ({
|
||||
link: !trim && x.media.link || null,
|
||||
large_url: x.media.large_url,
|
||||
|
@ -95,7 +99,7 @@ export async function serveIndex(ctx, path) {
|
|||
{ id: x.id, name: x.name, path: x.path }
|
||||
))
|
||||
))
|
||||
featured = await Article.getFeatured(['files', 'media', 'banner'])
|
||||
featured = await Article.getFeatured(['media', 'banner'])
|
||||
if (featured) {
|
||||
featured = mapArticle(true, featured.toJSON(), true, false)
|
||||
}
|
||||
|
@ -120,7 +124,7 @@ export async function serveIndex(ctx, path) {
|
|||
if (id) {
|
||||
let found
|
||||
if (path.startsWith('/article/')) {
|
||||
found = await Article.getSingle(id, ['media', 'parent', 'banner', 'files'], false, null, true)
|
||||
found = await Article.getSingle(id, ['media', 'parent', 'banner', 'files', 'staff'], false, null, true)
|
||||
if (found) {
|
||||
found = mapArticle(false, found.toJSON())
|
||||
}
|
||||
|
|
|
@ -26,9 +26,7 @@ const Staff = bookshelf.createModel({
|
|||
]),
|
||||
}, {
|
||||
// Hide password from any relations and include requests.
|
||||
publicFields: bookshelf.safeColumns([
|
||||
'fullname',
|
||||
]),
|
||||
publicFields: ['id', 'fullname'],
|
||||
|
||||
hash(password) {
|
||||
return new Promise((resolve, reject) =>
|
||||
|
|
|
@ -29,7 +29,7 @@ const AdminArticles = {
|
|||
return pagination.fetchPage(Article.getAllArticlesPagination({
|
||||
per_page: 10,
|
||||
page: this.lastpage,
|
||||
includes: ['parent'],
|
||||
includes: ['parent', 'staff'],
|
||||
}))
|
||||
.then(function(result) {
|
||||
vnode.state.articles = result.data
|
||||
|
@ -70,13 +70,10 @@ const AdminArticles = {
|
|||
name: '-- Frontpage --',
|
||||
}
|
||||
}
|
||||
let other = ''
|
||||
let className = ''
|
||||
if (new Date() < new Date(article.published_at)) {
|
||||
other = '(hidden)'
|
||||
className = 'rowhidden'
|
||||
} else if (article.is_featured) {
|
||||
other = '(featured)'
|
||||
className = 'rowfeatured'
|
||||
}
|
||||
return [
|
||||
|
@ -85,7 +82,7 @@ const AdminArticles = {
|
|||
m('td', m(m.route.Link, { href: parent.path }, parent.name)),
|
||||
m('td', m(m.route.Link, { href: '/article/' + article.path }, '/article/' + article.path)),
|
||||
m('td.right', article.published_at.replace('T', ' ').split('.')[0]),
|
||||
m('td.right', other),
|
||||
m('td.right', article.staff && article.staff.fullname || 'Admin'),
|
||||
m('td.right', m('button', { onclick: function() { vnode.state.removeArticle = article } }, 'Remove')),
|
||||
]),
|
||||
]
|
||||
|
@ -113,7 +110,7 @@ const AdminArticles = {
|
|||
m('th', 'Page'),
|
||||
m('th', 'Path'),
|
||||
m('th.right', 'Publish'),
|
||||
m('th.right', 'Other'),
|
||||
m('th.right', 'By'),
|
||||
m('th.right', 'Actions'),
|
||||
])
|
||||
),
|
||||
|
|
|
@ -40,6 +40,14 @@ article.editarticle {
|
|||
height: 300px;
|
||||
}
|
||||
|
||||
label.slim {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
input.slim {
|
||||
font-size: 0.8em;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
height: 300px;
|
||||
position: relative;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const Authentication = require('../authentication')
|
||||
const FileUpload = require('../widgets/fileupload')
|
||||
const Staff = require('../api/staff')
|
||||
const Froala = require('./froala')
|
||||
const Page = require('../api/page')
|
||||
const File = require('../api/file')
|
||||
|
@ -30,6 +31,18 @@ const EditArticle = {
|
|||
oninit: function(vnode) {
|
||||
this.froala = null
|
||||
this.loadedFroala = Froala.loadedFroala
|
||||
this.staffers = []
|
||||
|
||||
Staff.getAllStaff()
|
||||
.then(function(result) {
|
||||
vnode.state.staffers = result
|
||||
})
|
||||
.catch(function(err) {
|
||||
vnode.state.error = err.message
|
||||
})
|
||||
.then(function() {
|
||||
m.redraw()
|
||||
})
|
||||
|
||||
if (!this.loadedFroala) {
|
||||
Froala.createFroalaScript()
|
||||
|
@ -121,6 +134,10 @@ const EditArticle = {
|
|||
}
|
||||
},
|
||||
|
||||
updateStaffer: function(e) {
|
||||
this.article.staff_id = Number(e.currentTarget.value)
|
||||
},
|
||||
|
||||
mediaUploaded: function(type, media) {
|
||||
this.article[type] = media
|
||||
},
|
||||
|
@ -159,6 +176,7 @@ const EditArticle = {
|
|||
media_id: this.article.media && this.article.media.id,
|
||||
published_at: new Date(this.article.published_at),
|
||||
is_featured: this.article.is_featured,
|
||||
staff_id: this.article.staff_id,
|
||||
})
|
||||
} else {
|
||||
promise = Article.createArticle({
|
||||
|
@ -170,6 +188,7 @@ const EditArticle = {
|
|||
media_id: this.article.media && this.article.media.id,
|
||||
published_at: new Date(this.article.published_at),
|
||||
is_featured: this.article.is_featured,
|
||||
staff_id: this.article.staff_id,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -225,8 +244,20 @@ const EditArticle = {
|
|||
return out
|
||||
},
|
||||
|
||||
getStaffers: function() {
|
||||
if (!this.article.staff_id) {
|
||||
this.article.staff_id = 1
|
||||
}
|
||||
let out = []
|
||||
this.staffers.forEach(function(item) {
|
||||
out.push({ id: item.id, name: item.fullname })
|
||||
})
|
||||
return out
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
const parents = this.getFlatTree()
|
||||
const staffers = this.getStaffers()
|
||||
return (
|
||||
this.loading ?
|
||||
m('div.loading-spinner')
|
||||
|
@ -265,18 +296,18 @@ const EditArticle = {
|
|||
m('select', {
|
||||
onchange: this.updateParent.bind(this),
|
||||
}, parents.map(function(item) { return m('option', { value: item.id || -1, selected: item.id === vnode.state.article.parent_id }, item.name) })),
|
||||
m('label', 'Path'),
|
||||
m('input', {
|
||||
type: 'text',
|
||||
value: this.article.path,
|
||||
oninput: this.updateValue.bind(this, 'path'),
|
||||
}),
|
||||
m('label', 'Name'),
|
||||
m('input', {
|
||||
type: 'text',
|
||||
value: this.article.name,
|
||||
oninput: this.updateValue.bind(this, 'name'),
|
||||
}),
|
||||
m('label.slim', 'Path'),
|
||||
m('input.slim', {
|
||||
type: 'text',
|
||||
value: this.article.path,
|
||||
oninput: this.updateValue.bind(this, 'path'),
|
||||
}),
|
||||
m('label', 'Description'),
|
||||
(
|
||||
this.loadedFroala ?
|
||||
|
@ -289,12 +320,16 @@ const EditArticle = {
|
|||
})
|
||||
: null
|
||||
),
|
||||
m('label', 'Publish at'),
|
||||
m('label', 'Published at'),
|
||||
m('input', {
|
||||
type: 'datetime-local',
|
||||
value: this.article.published_at,
|
||||
oninput: this.updateValue.bind(this, 'published_at'),
|
||||
}),
|
||||
m('label', 'Published by'),
|
||||
m('select', {
|
||||
onchange: this.updateStaffer.bind(this),
|
||||
}, staffers.map(function(item) { return m('option', { value: item.id, selected: item.id === vnode.state.article.staff_id }, item.name) })),
|
||||
m('label', 'Make featured'),
|
||||
m('input', {
|
||||
type: 'checkbox',
|
||||
|
|
|
@ -41,6 +41,6 @@ exports.getAllPageArticlesPagination = function(pageId, options) {
|
|||
exports.getArticle = function(id) {
|
||||
return common.sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/articles/public/' + id + '?includes=media,parent,banner,files',
|
||||
url: '/api/articles/public/' + id + '?includes=media,parent,banner,files,staff',
|
||||
})
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ const Article = {
|
|||
this.loading ?
|
||||
m('div.loading-spinner')
|
||||
: m('article.article', [
|
||||
this.article.parent ? m('div.goback', ['« ', m(m.route.Link, { href: '/page/' + this.article.parent.path }, this.article.parent.name)]) : null,
|
||||
m('header', m('h1', this.article.name)),
|
||||
m('.fr-view', [
|
||||
this.article.media
|
||||
|
@ -89,6 +90,13 @@ const Article = {
|
|||
return m(Fileinfo, { file: file })
|
||||
})
|
||||
: null),
|
||||
m('div.entrymeta', [
|
||||
'Posted ',
|
||||
(this.article.parent ? 'in' : ''),
|
||||
(this.article.parent ? m(m.route.Link, { href: '/page/' + this.article.parent.path }, this.article.parent.name) : null),
|
||||
'at ' + (this.article.published_at.replace('T', ' ').split('.')[0]).substr(0, 16),
|
||||
' by ' + (this.article.staff && this.article.staff.fullname || 'Admin'),
|
||||
]),
|
||||
]),
|
||||
Authentication.currentUser
|
||||
? m('div.admin-actions', [
|
||||
|
|
|
@ -26,7 +26,7 @@ article.article {
|
|||
|
||||
.fr-view {
|
||||
margin: 0 20px;
|
||||
padding: 20px 20px 60px;
|
||||
padding: 20px 20px 10px;
|
||||
width: calc(100% - 40px);
|
||||
max-width: 800px;
|
||||
flex: 2 0 0;
|
||||
|
@ -73,6 +73,34 @@ article.article {
|
|||
min-height: 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.goback {
|
||||
width: calc(100% - 40px);
|
||||
max-width: 800px;
|
||||
align-self: center;
|
||||
padding: 10px 5px 0;
|
||||
margin-bottom: -10px;
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: $secondary-dark-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.entrymeta {
|
||||
flex-grow: 2;
|
||||
font-size: 11px;
|
||||
color: $meta-fg;
|
||||
font-weight: bold;
|
||||
margin: 40px 0 0;
|
||||
|
||||
a {
|
||||
color: $secondary-dark-bg;
|
||||
margin: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.darkmodeon {
|
||||
|
@ -88,5 +116,13 @@ article.article {
|
|||
.fr-view {
|
||||
background: $dark_newsitem-bg;
|
||||
}
|
||||
|
||||
.opencomments {
|
||||
color: $dark_secondary-dark-bg;
|
||||
}
|
||||
|
||||
.goback a {
|
||||
color: $dark_secondary-dark-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ const Frontpage = {
|
|||
return Pagination.fetchPage(Article.getAllArticlesPagination({
|
||||
per_page: 10,
|
||||
page: this.lastpage,
|
||||
includes: ['parent', 'files', 'media', 'banner'],
|
||||
includes: ['parent', 'files', 'media', 'banner', 'staff'],
|
||||
}))
|
||||
.then(function(result) {
|
||||
vnode.state.articles = result.data
|
||||
|
|
|
@ -58,6 +58,7 @@ newsentry {
|
|||
fileinfo {
|
||||
padding: 0 5px;
|
||||
margin-bottom: 5px;
|
||||
display: block;
|
||||
|
||||
&.slim {
|
||||
padding: 0;
|
||||
|
|
|
@ -32,7 +32,6 @@ const Newsentry = {
|
|||
: vnode.attrs.description
|
||||
? m('span.entrydescription', Newsentry.strip(vnode.attrs.description))
|
||||
: null),
|
||||
m('span.entrymeta', 'Posted ' + vnode.attrs.published_at.replace('T', ' ').split('.')[0]),
|
||||
]),
|
||||
])
|
||||
},
|
||||
|
|
|
@ -30,6 +30,7 @@ const Newsitem = {
|
|||
(vnode.attrs.parent ? 'in' : ''),
|
||||
(vnode.attrs.parent ? m(m.route.Link, { href: '/page/' + vnode.attrs.parent.path }, vnode.attrs.parent.name) : null),
|
||||
'at ' + (vnode.attrs.published_at.replace('T', ' ').split('.')[0]).substr(0, 16),
|
||||
' by ' + (vnode.attrs.staff && vnode.attrs.staff.fullname || 'Admin'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
|
Loading…
Reference in a new issue