Finalised official release 2.0.0
This commit is contained in:
parent
ecbf015cb6
commit
f760ec6d8b
14 changed files with 203 additions and 37 deletions
|
@ -47,15 +47,55 @@ const Article = bookshelf.createModel({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
getSingle(id, withRelated = [], require = true, ctx = null) {
|
getAll(ctx, where = {}, withRelated = [], orderBy = 'id', limitToday = false) {
|
||||||
return this.query(qb => {
|
return this.query(qb => {
|
||||||
qb.where({ id: Number(id) || 0 })
|
this.baseQueryAll(ctx, qb, where, orderBy)
|
||||||
.orWhere({ path: id })
|
if (limitToday) {
|
||||||
|
qb.where('published_at', '<=', (new Date()).toISOString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fetchPage({
|
||||||
|
pageSize: ctx.state.pagination.perPage,
|
||||||
|
page: ctx.state.pagination.page,
|
||||||
|
withRelated,
|
||||||
|
ctx: ctx,
|
||||||
|
})
|
||||||
|
.then(result => {
|
||||||
|
ctx.state.pagination.total = result.pagination.rowCount
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getSingle(id, withRelated = [], require = true, ctx = null, limitToday = false) {
|
||||||
|
return this.query(qb => {
|
||||||
|
qb.where(subq => {
|
||||||
|
subq.where({ id: Number(id) || 0 })
|
||||||
|
.orWhere({ path: id })
|
||||||
|
})
|
||||||
|
if (limitToday && (!ctx || !ctx.state.user || ctx.state.user.level < 10)) {
|
||||||
|
qb.where('published_at', '<=', (new Date()).toISOString())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.fetch({ require, withRelated, ctx })
|
.fetch({ require, withRelated, ctx })
|
||||||
},
|
},
|
||||||
|
|
||||||
getAllFromPage(ctx, pageId, withRelated = [], orderBy = 'id') {
|
async getFeatured(withRelated = [], ctx = null) {
|
||||||
|
let data = await this.query(qb => {
|
||||||
|
qb.where({ is_featured: true })
|
||||||
|
.where('published_at', '<=', (new Date()).toISOString())
|
||||||
|
})
|
||||||
|
.fetch({ require: false, withRelated, ctx })
|
||||||
|
if (!data) {
|
||||||
|
data = await this.query(qb => {
|
||||||
|
qb.where('published_at', '<=', (new Date()).toISOString())
|
||||||
|
.whereNotNull('banner_id')
|
||||||
|
})
|
||||||
|
.fetch({ require: false, withRelated, ctx })
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
|
||||||
|
getAllFromPage(ctx, pageId, withRelated = [], orderBy = 'id', limitToday = false) {
|
||||||
return this.query(qb => {
|
return this.query(qb => {
|
||||||
this.baseQueryAll(ctx, qb, {}, orderBy)
|
this.baseQueryAll(ctx, qb, {}, orderBy)
|
||||||
qb.leftOuterJoin('pages', 'articles.parent_id', 'pages.id')
|
qb.leftOuterJoin('pages', 'articles.parent_id', 'pages.id')
|
||||||
|
@ -63,6 +103,9 @@ const Article = bookshelf.createModel({
|
||||||
subq.where('pages.id', pageId)
|
subq.where('pages.id', pageId)
|
||||||
.orWhere('pages.parent_id', pageId)
|
.orWhere('pages.parent_id', pageId)
|
||||||
})
|
})
|
||||||
|
if (limitToday) {
|
||||||
|
qb.where('published_at', '<=', (new Date()).toISOString())
|
||||||
|
}
|
||||||
qb.select('articles.*')
|
qb.select('articles.*')
|
||||||
})
|
})
|
||||||
.fetchPage({
|
.fetchPage({
|
||||||
|
@ -77,9 +120,18 @@ const Article = bookshelf.createModel({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setAllUnfeatured() {
|
||||||
|
return bookshelf.knex('articles')
|
||||||
|
.where({ is_featured: true })
|
||||||
|
.update({
|
||||||
|
is_featured: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
getFrontpageArticles(page = 1) {
|
getFrontpageArticles(page = 1) {
|
||||||
return this.query(qb => {
|
return this.query(qb => {
|
||||||
qb.orderBy('updated_at', 'DESC')
|
qb.orderBy('published_at', 'DESC')
|
||||||
|
.where('published_at', '<=', (new Date()).toISOString())
|
||||||
})
|
})
|
||||||
.fetchPage({
|
.fetchPage({
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
|
|
|
@ -13,14 +13,14 @@ export default class ArticleRoutes {
|
||||||
async getAllArticles(ctx) {
|
async getAllArticles(ctx) {
|
||||||
await this.security.ensureIncludes(ctx)
|
await this.security.ensureIncludes(ctx)
|
||||||
|
|
||||||
ctx.body = await this.Article.getAll(ctx, { }, ctx.state.filter.includes, ctx.query.sort || '-id')
|
ctx.body = await this.Article.getAll(ctx, { }, ctx.state.filter.includes, ctx.query.sort || '-published_at')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GET: /api/pages/:pageId/articles */
|
/** GET: /api/pages/:pageId/articles */
|
||||||
async getAllPageArticles(ctx) {
|
async getAllPageArticles(ctx) {
|
||||||
await this.security.ensureIncludes(ctx)
|
await this.security.ensureIncludes(ctx)
|
||||||
|
|
||||||
ctx.body = await this.Article.getAllFromPage(ctx, ctx.params.pageId, ctx.state.filter.includes, ctx.query.sort || '-id')
|
ctx.body = await this.Article.getAllFromPage(ctx, ctx.params.pageId, ctx.state.filter.includes, ctx.query.sort || '-published_at')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GET: /api/articles/:id */
|
/** GET: /api/articles/:id */
|
||||||
|
@ -30,6 +30,27 @@ export default class ArticleRoutes {
|
||||||
ctx.body = await this.Article.getSingle(ctx.params.id, ctx.state.filter.includes, true, ctx)
|
ctx.body = await this.Article.getSingle(ctx.params.id, ctx.state.filter.includes, true, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** GET: /api/articles/public */
|
||||||
|
async getPublicAllArticles(ctx) {
|
||||||
|
await this.security.ensureIncludes(ctx)
|
||||||
|
|
||||||
|
ctx.body = await this.Article.getAll(ctx, { }, ctx.state.filter.includes, ctx.query.sort || '-published_at', true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET: /api/pages/:pageId/articles/public */
|
||||||
|
async getPublicAllPageArticles(ctx) {
|
||||||
|
await this.security.ensureIncludes(ctx)
|
||||||
|
|
||||||
|
ctx.body = await this.Article.getAllFromPage(ctx, ctx.params.pageId, ctx.state.filter.includes, ctx.query.sort || '-published_at', true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET: /api/articles/public/:id */
|
||||||
|
async getPublicSingleArticle(ctx) {
|
||||||
|
await this.security.ensureIncludes(ctx)
|
||||||
|
|
||||||
|
ctx.body = await this.Article.getSingle(ctx.params.id, ctx.state.filter.includes, true, ctx, true)
|
||||||
|
}
|
||||||
|
|
||||||
/** POST: /api/articles */
|
/** POST: /api/articles */
|
||||||
async createArticle(ctx) {
|
async createArticle(ctx) {
|
||||||
await this.security.validUpdate(ctx)
|
await this.security.validUpdate(ctx)
|
||||||
|
@ -41,6 +62,10 @@ export default class ArticleRoutes {
|
||||||
async updateArticle(ctx) {
|
async updateArticle(ctx) {
|
||||||
await this.security.validUpdate(ctx)
|
await this.security.validUpdate(ctx)
|
||||||
|
|
||||||
|
if (ctx.request.body.is_featured) {
|
||||||
|
await Article.setAllUnfeatured()
|
||||||
|
}
|
||||||
|
|
||||||
let page = await this.Article.getSingle(ctx.params.id)
|
let page = await this.Article.getSingle(ctx.params.id)
|
||||||
|
|
||||||
page.set(ctx.request.body)
|
page.set(ctx.request.body)
|
||||||
|
|
|
@ -12,6 +12,8 @@ const validFields = [
|
||||||
'parent_id',
|
'parent_id',
|
||||||
'media_id',
|
'media_id',
|
||||||
'banner_id',
|
'banner_id',
|
||||||
|
'published_at',
|
||||||
|
'is_featured',
|
||||||
]
|
]
|
||||||
|
|
||||||
export async function ensureIncludes(ctx) {
|
export async function ensureIncludes(ctx) {
|
||||||
|
@ -34,4 +36,8 @@ export async function validUpdate(ctx) {
|
||||||
if (out.length > 0) {
|
if (out.length > 0) {
|
||||||
ctx.throw(422, `Body had following invalid properties: ${out.join(', ')}`)
|
ctx.throw(422, `Body had following invalid properties: ${out.join(', ')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.request.body.published_at) {
|
||||||
|
ctx.request.body.published_at = new Date(ctx.request.body.published_at)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,12 @@ router.put('/api/pages/:id', restrict(access.Manager), page.updatePage.bind(page
|
||||||
router.del('/api/pages/:id', restrict(access.Manager), page.removePage.bind(page))
|
router.del('/api/pages/:id', restrict(access.Manager), page.removePage.bind(page))
|
||||||
|
|
||||||
const article = new ArticleRoutes()
|
const article = new ArticleRoutes()
|
||||||
router.get('/api/articles', article.getAllArticles.bind(article))
|
router.get('/api/articles', restrict(access.Manager), article.getAllArticles.bind(article))
|
||||||
router.get('/api/pages/:pageId/articles', article.getAllPageArticles.bind(article))
|
router.get('/api/articles/public', article.getPublicAllArticles.bind(article))
|
||||||
router.get('/api/articles/:id', article.getSingleArticle.bind(article))
|
router.get('/api/articles/public/:id', article.getPublicSingleArticle.bind(article))
|
||||||
|
router.get('/api/pages/:pageId/articles/public', article.getPublicAllPageArticles.bind(article))
|
||||||
|
router.get('/api/pages/:pageId/articles', restrict(access.Manager), article.getAllPageArticles.bind(article))
|
||||||
|
router.get('/api/articles/:id', restrict(access.Manager), article.getSingleArticle.bind(article))
|
||||||
router.post('/api/articles', restrict(access.Manager), article.createArticle.bind(article))
|
router.post('/api/articles', restrict(access.Manager), article.createArticle.bind(article))
|
||||||
router.put('/api/articles/:id', restrict(access.Manager), article.updateArticle.bind(article))
|
router.put('/api/articles/:id', restrict(access.Manager), article.updateArticle.bind(article))
|
||||||
router.del('/api/articles/:id', restrict(access.Manager), article.removeArticle.bind(article))
|
router.del('/api/articles/:id', restrict(access.Manager), article.removeArticle.bind(article))
|
||||||
|
|
|
@ -3,6 +3,7 @@ import defaults from './defaults.mjs'
|
||||||
import access from './access/index.mjs'
|
import access from './access/index.mjs'
|
||||||
import { restrict } from './access/middleware.mjs'
|
import { restrict } from './access/middleware.mjs'
|
||||||
import { serveIndex } from './serveindex.mjs'
|
import { serveIndex } from './serveindex.mjs'
|
||||||
|
import config from './config.mjs'
|
||||||
|
|
||||||
const restrictAdmin = restrict(access.Manager)
|
const restrictAdmin = restrict(access.Manager)
|
||||||
|
|
||||||
|
@ -36,8 +37,13 @@ export function serve(docRoot, pathname, options = {}) {
|
||||||
if (filepath.indexOf('admin') >= 0
|
if (filepath.indexOf('admin') >= 0
|
||||||
&& (filepath.indexOf('js') >= 0
|
&& (filepath.indexOf('js') >= 0
|
||||||
|| filepath.indexOf('css') >= 0)) {
|
|| filepath.indexOf('css') >= 0)) {
|
||||||
await restrictAdmin(ctx)
|
if (filepath.indexOf('.map') === -1) {
|
||||||
ctx.set('Cache-Control', 'no-store, no-cache, must-revalidate')
|
await restrictAdmin(ctx)
|
||||||
|
ctx.set('Cache-Control', 'no-store, no-cache, must-revalidate')
|
||||||
|
} else if (config.get('NODE_ENV') !== 'development') {
|
||||||
|
ctx.status = 404
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return send(ctx, filepath, opts).catch((er) => {
|
return send(ctx, filepath, opts).catch((er) => {
|
||||||
|
|
|
@ -77,6 +77,7 @@ export async function serveIndex(ctx, path) {
|
||||||
let tree = null
|
let tree = null
|
||||||
let data = null
|
let data = null
|
||||||
let links = null
|
let links = null
|
||||||
|
let featured = null
|
||||||
let url = frontend + ctx.request.url
|
let url = frontend + ctx.request.url
|
||||||
let image = frontend + '/assets/img/heart.jpg'
|
let image = frontend + '/assets/img/heart.jpg'
|
||||||
let title = 'NFP Moe - Anime/Manga translation group'
|
let title = 'NFP Moe - Anime/Manga translation group'
|
||||||
|
@ -88,6 +89,11 @@ export async function serveIndex(ctx, path) {
|
||||||
{ id: x.id, name: x.name, path: x.path }
|
{ id: x.id, name: x.name, path: x.path }
|
||||||
))
|
))
|
||||||
))
|
))
|
||||||
|
featured = await Article.getFeatured(['files', 'media', 'banner'])
|
||||||
|
if (featured) {
|
||||||
|
featured = mapArticle(featured.toJSON())
|
||||||
|
}
|
||||||
|
|
||||||
if (path === '/') {
|
if (path === '/') {
|
||||||
data = await Article.getFrontpageArticles(Number(ctx.query.page || '1'))
|
data = await Article.getFrontpageArticles(Number(ctx.query.page || '1'))
|
||||||
|
|
||||||
|
@ -108,26 +114,30 @@ export async function serveIndex(ctx, path) {
|
||||||
if (id) {
|
if (id) {
|
||||||
let found
|
let found
|
||||||
if (path.startsWith('/article/')) {
|
if (path.startsWith('/article/')) {
|
||||||
found = await Article.getSingle(id, ['media', 'parent', 'banner', 'files'])
|
found = await Article.getSingle(id, ['media', 'parent', 'banner', 'files'], false, null, true)
|
||||||
found = mapArticle(found.toJSON())
|
if (found) {
|
||||||
|
found = mapArticle(found.toJSON())
|
||||||
|
}
|
||||||
data = found
|
data = found
|
||||||
} else {
|
} else {
|
||||||
found = await Page.getSingle(id, ['media', 'banner', 'children'])
|
found = await Page.getSingle(id, ['media', 'banner', 'children'])
|
||||||
found = mapPage(found.toJSON())
|
found = mapPage(found.toJSON())
|
||||||
data = found
|
data = found
|
||||||
}
|
}
|
||||||
if (found.media) {
|
if (found) {
|
||||||
image = found.media.large_url
|
if (found.media) {
|
||||||
} else if (found.banner) {
|
image = found.media.large_url
|
||||||
image = found.banner.large_url
|
} else if (found.banner) {
|
||||||
}
|
image = found.banner.large_url
|
||||||
if (found.description) {
|
}
|
||||||
description = striptags(found.description)
|
if (found.description) {
|
||||||
}
|
description = striptags(found.description)
|
||||||
if (found.parent) {
|
}
|
||||||
title = found.name + ' - ' + found.parent.name + ' - NFP Moe'
|
if (found.parent) {
|
||||||
} else {
|
title = found.name + ' - ' + found.parent.name + ' - NFP Moe'
|
||||||
title = found.name + ' - NFP Moe'
|
} else {
|
||||||
|
title = found.name + ' - NFP Moe'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +151,7 @@ export async function serveIndex(ctx, path) {
|
||||||
tree: JSON.stringify(tree),
|
tree: JSON.stringify(tree),
|
||||||
data: JSON.stringify(data),
|
data: JSON.stringify(data),
|
||||||
links: JSON.stringify(links),
|
links: JSON.stringify(links),
|
||||||
|
featured: JSON.stringify(featured),
|
||||||
url: url,
|
url: url,
|
||||||
image: image,
|
image: image,
|
||||||
title: title,
|
title: title,
|
||||||
|
|
|
@ -70,12 +70,22 @@ const AdminArticles = {
|
||||||
name: '-- Frontpage --',
|
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 [
|
return [
|
||||||
m('tr', [
|
m('tr', { class: className }, [
|
||||||
m('td', m(m.route.Link, { href: '/admin/articles/' + article.id }, article.name)),
|
m('td', m(m.route.Link, { href: '/admin/articles/' + article.id }, article.name)),
|
||||||
m('td', m(m.route.Link, { href: parent.path }, parent.name)),
|
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', m(m.route.Link, { href: '/article/' + article.path }, '/article/' + article.path)),
|
||||||
m('td.right', article.updated_at.replace('T', ' ').split('.')[0]),
|
m('td.right', article.published_at.replace('T', ' ').split('.')[0]),
|
||||||
|
m('td.right', other),
|
||||||
m('td.right', m('button', { onclick: function() { vnode.state.removeArticle = article } }, 'Remove')),
|
m('td.right', m('button', { onclick: function() { vnode.state.removeArticle = article } }, 'Remove')),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
@ -102,7 +112,8 @@ const AdminArticles = {
|
||||||
m('th', 'Title'),
|
m('th', 'Title'),
|
||||||
m('th', 'Page'),
|
m('th', 'Page'),
|
||||||
m('th', 'Path'),
|
m('th', 'Path'),
|
||||||
m('th.right', 'Updated'),
|
m('th.right', 'Publish'),
|
||||||
|
m('th.right', 'Other'),
|
||||||
m('th.right', 'Actions'),
|
m('th.right', 'Actions'),
|
||||||
])
|
])
|
||||||
),
|
),
|
||||||
|
|
|
@ -46,6 +46,16 @@ article.editarticle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
tr.rowhidden td {
|
||||||
|
background: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.rowfeatured td {
|
||||||
|
background: hsl(120, 60%, 85%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,8 @@ const EditArticle = {
|
||||||
media: null,
|
media: null,
|
||||||
banner: null,
|
banner: null,
|
||||||
files: [],
|
files: [],
|
||||||
|
is_featured: false,
|
||||||
|
published_at: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
this.editedPath = false
|
this.editedPath = false
|
||||||
this.loadedFroala = Froala.loadedFroala
|
this.loadedFroala = Froala.loadedFroala
|
||||||
|
@ -73,6 +75,7 @@ const EditArticle = {
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
vnode.state.editedPath = true
|
vnode.state.editedPath = true
|
||||||
vnode.state.article = result
|
vnode.state.article = result
|
||||||
|
EditArticle.parsePublishedAt(vnode, null)
|
||||||
document.title = 'Editing: ' + result.name + ' - Admin NFP Moe'
|
document.title = 'Editing: ' + result.name + ' - Admin NFP Moe'
|
||||||
})
|
})
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
|
@ -86,6 +89,7 @@ const EditArticle = {
|
||||||
m.redraw()
|
m.redraw()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
EditArticle.parsePublishedAt(vnode, new Date())
|
||||||
document.title = 'Create Article - Admin NFP Moe'
|
document.title = 'Create Article - Admin NFP Moe'
|
||||||
if (vnode.state.froala) {
|
if (vnode.state.froala) {
|
||||||
vnode.state.froala.html.set(this.article.description)
|
vnode.state.froala.html.set(this.article.description)
|
||||||
|
@ -93,8 +97,16 @@ const EditArticle = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
parsePublishedAt: function(vnode, date) {
|
||||||
|
vnode.state.article.published_at = ((date && date.toISOString() || vnode.state.article.published_at).split('.')[0]).substr(0, 16)
|
||||||
|
},
|
||||||
|
|
||||||
updateValue: function(name, e) {
|
updateValue: function(name, e) {
|
||||||
this.article[name] = e.currentTarget.value
|
if (name === 'is_featured') {
|
||||||
|
this.article[name] = e.currentTarget.checked
|
||||||
|
} else {
|
||||||
|
this.article[name] = e.currentTarget.value
|
||||||
|
}
|
||||||
if (name === 'path') {
|
if (name === 'path') {
|
||||||
this.editedPath = true
|
this.editedPath = true
|
||||||
} else if (name === 'name' && !this.editedPath) {
|
} else if (name === 'name' && !this.editedPath) {
|
||||||
|
@ -145,6 +157,8 @@ const EditArticle = {
|
||||||
description: this.article.description,
|
description: this.article.description,
|
||||||
banner_id: this.article.banner && this.article.banner.id,
|
banner_id: this.article.banner && this.article.banner.id,
|
||||||
media_id: this.article.media && this.article.media.id,
|
media_id: this.article.media && this.article.media.id,
|
||||||
|
published_at: new Date(this.article.published_at),
|
||||||
|
is_featured: this.article.is_featured,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
promise = Article.createArticle({
|
promise = Article.createArticle({
|
||||||
|
@ -154,6 +168,8 @@ const EditArticle = {
|
||||||
description: this.article.description,
|
description: this.article.description,
|
||||||
banner_id: this.article.banner && this.article.banner.id,
|
banner_id: this.article.banner && this.article.banner.id,
|
||||||
media_id: this.article.media && this.article.media.id,
|
media_id: this.article.media && this.article.media.id,
|
||||||
|
published_at: new Date(this.article.published_at),
|
||||||
|
is_featured: this.article.is_featured,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +179,7 @@ const EditArticle = {
|
||||||
res.banner = vnode.state.article.banner
|
res.banner = vnode.state.article.banner
|
||||||
res.files = vnode.state.article.files
|
res.files = vnode.state.article.files
|
||||||
vnode.state.article = res
|
vnode.state.article = res
|
||||||
|
EditArticle.parsePublishedAt(vnode, null)
|
||||||
} else {
|
} else {
|
||||||
m.route.set('/admin/articles/' + res.id)
|
m.route.set('/admin/articles/' + res.id)
|
||||||
}
|
}
|
||||||
|
@ -248,6 +265,12 @@ const EditArticle = {
|
||||||
m('select', {
|
m('select', {
|
||||||
onchange: this.updateParent.bind(this),
|
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) })),
|
}, 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('label', 'Name'),
|
||||||
m('input', {
|
m('input', {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
@ -266,11 +289,17 @@ const EditArticle = {
|
||||||
})
|
})
|
||||||
: null
|
: null
|
||||||
),
|
),
|
||||||
m('label', 'Path'),
|
m('label', 'Publish at'),
|
||||||
m('input', {
|
m('input', {
|
||||||
type: 'text',
|
type: 'datetime-local',
|
||||||
value: this.article.path,
|
value: this.article.published_at,
|
||||||
oninput: this.updateValue.bind(this, 'path'),
|
oninput: this.updateValue.bind(this, 'published_at'),
|
||||||
|
}),
|
||||||
|
m('label', 'Make featured'),
|
||||||
|
m('input', {
|
||||||
|
type: 'checkbox',
|
||||||
|
checked: this.article.is_featured,
|
||||||
|
oninput: this.updateValue.bind(this, 'is_featured'),
|
||||||
}),
|
}),
|
||||||
m('div.loading-spinner', { hidden: this.loadedFroala }),
|
m('div.loading-spinner', { hidden: this.loadedFroala }),
|
||||||
m('input', {
|
m('input', {
|
||||||
|
|
|
@ -16,7 +16,7 @@ exports.getAllArticlesPagination = function(options) {
|
||||||
extra += '&includes=' + options.includes.join(',')
|
extra += '&includes=' + options.includes.join(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
return '/api/articles?' + extra
|
return '/api/articles/public?' + extra
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getAllPageArticlesPagination = function(pageId, options) {
|
exports.getAllPageArticlesPagination = function(pageId, options) {
|
||||||
|
@ -35,12 +35,12 @@ exports.getAllPageArticlesPagination = function(pageId, options) {
|
||||||
extra += '&includes=' + options.includes.join(',')
|
extra += '&includes=' + options.includes.join(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
return '/api/pages/' + pageId + '/articles?' + extra
|
return '/api/pages/' + pageId + '/articles/public?' + extra
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getArticle = function(id) {
|
exports.getArticle = function(id) {
|
||||||
return common.sendRequest({
|
return common.sendRequest({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/api/articles/' + id + '?includes=media,parent,banner,files',
|
url: '/api/articles/public/' + id + '?includes=media,parent,banner,files',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,7 @@ form {
|
||||||
|
|
||||||
input[type=text],
|
input[type=text],
|
||||||
input[type=password],
|
input[type=password],
|
||||||
|
input[type=datetime-local],
|
||||||
select,
|
select,
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -13,6 +13,12 @@ const Frontpage = {
|
||||||
this.featured = null
|
this.featured = null
|
||||||
this.links = null
|
this.links = null
|
||||||
|
|
||||||
|
if (window.__nfpfeatured) {
|
||||||
|
this.featured = window.__nfpfeatured
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(this.featured)
|
||||||
|
|
||||||
if (window.__nfpdata
|
if (window.__nfpdata
|
||||||
&& window.__nfplinks) {
|
&& window.__nfplinks) {
|
||||||
this.links = window.__nfplinks
|
this.links = window.__nfplinks
|
||||||
|
|
|
@ -76,6 +76,11 @@ exports.up = function up(knex, Promise) {
|
||||||
table.boolean('is_deleted')
|
table.boolean('is_deleted')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.default(false)
|
.default(false)
|
||||||
|
table.timestamp('published_at')
|
||||||
|
.defaultTo(knex.fn.now())
|
||||||
|
table.boolean('is_featured')
|
||||||
|
.notNullable()
|
||||||
|
.default(false)
|
||||||
table.timestamps()
|
table.timestamps()
|
||||||
}),
|
}),
|
||||||
knex.schema.createTable('files', function(table) {
|
knex.schema.createTable('files', function(table) {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
if (localStorage.getItem('darkmode')) {document.body.className = 'darkmodeon';}
|
if (localStorage.getItem('darkmode')) {document.body.className = 'darkmodeon';}
|
||||||
window.__nfptree = {{=it.tree}};
|
window.__nfptree = {{=it.tree}};
|
||||||
|
window.__nfpfeatured = {{=it.featured}};
|
||||||
window.__nfpdata = {{=it.data}};
|
window.__nfpdata = {{=it.data}};
|
||||||
window.__nfplinks = {{=it.links}};
|
window.__nfplinks = {{=it.links}};
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in a new issue