Finished base articles and pages
This commit is contained in:
parent
8e0b4c29e1
commit
f12b440c19
30 changed files with 850 additions and 580 deletions
|
@ -1,6 +1,7 @@
|
||||||
import { parseFiles } from '../file/util.mjs'
|
import { parseFiles } from '../file/util.mjs'
|
||||||
import { parseArticles, parseArticle } from './util.mjs'
|
import { parseArticles, parseArticle } from './util.mjs'
|
||||||
import { upload } from '../media/upload.mjs'
|
import { upload } from '../media/upload.mjs'
|
||||||
|
import { mediaToDatabase } from '../media/util.mjs'
|
||||||
|
|
||||||
export default class ArticleRoutes {
|
export default class ArticleRoutes {
|
||||||
constructor(opts = {}) {
|
constructor(opts = {}) {
|
||||||
|
@ -11,7 +12,7 @@ export default class ArticleRoutes {
|
||||||
|
|
||||||
/** GET: /api/articles/[path] */
|
/** GET: /api/articles/[path] */
|
||||||
async getArticle(ctx) {
|
async getArticle(ctx) {
|
||||||
let res = await ctx.db.safeCallProc('common.article_get_single', [ctx.params.path])
|
let res = await ctx.db.safeCallProc('article_get_single', [ctx.params.path])
|
||||||
|
|
||||||
let out = {
|
let out = {
|
||||||
article: parseArticle(res.results[0][0]),
|
article: parseArticle(res.results[0][0]),
|
||||||
|
@ -23,7 +24,7 @@ export default class ArticleRoutes {
|
||||||
|
|
||||||
/** GET: /api/auth/articles */
|
/** GET: /api/auth/articles */
|
||||||
async auth_getAllArticles(ctx) {
|
async auth_getAllArticles(ctx) {
|
||||||
let res = await ctx.db.safeCallProc('common.article_auth_get_all', [
|
let res = await ctx.db.safeCallProc('article_auth_get_all', [
|
||||||
ctx.state.auth_token,
|
ctx.state.auth_token,
|
||||||
Math.max(ctx.query.get('page') || 1, 1),
|
Math.max(ctx.query.get('page') || 1, 1),
|
||||||
Math.min(ctx.query.get('per_page') || 10, 25)
|
Math.min(ctx.query.get('per_page') || 10, 25)
|
||||||
|
@ -40,7 +41,7 @@ export default class ArticleRoutes {
|
||||||
async private_getUpdateArticle(ctx, body = null, banner = null, media = null) {
|
async private_getUpdateArticle(ctx, body = null, banner = null, media = null) {
|
||||||
let params = [
|
let params = [
|
||||||
ctx.state.auth_token,
|
ctx.state.auth_token,
|
||||||
ctx.params.path
|
ctx.params.id === '0' ? null : ctx.params.id
|
||||||
]
|
]
|
||||||
if (body) {
|
if (body) {
|
||||||
params = params.concat([
|
params = params.concat([
|
||||||
|
@ -53,100 +54,28 @@ export default class ArticleRoutes {
|
||||||
body.is_featured === 'true' ? 1 : 0,
|
body.is_featured === 'true' ? 1 : 0,
|
||||||
0,
|
0,
|
||||||
])
|
])
|
||||||
if (banner) {
|
params = params.concat(mediaToDatabase(banner, body.remove_banner === 'true'))
|
||||||
params = params.concat([
|
params = params.concat(mediaToDatabase(media, body.remove_media === 'true'))
|
||||||
banner.filename,
|
|
||||||
banner.type,
|
|
||||||
banner.path,
|
|
||||||
banner.size,
|
|
||||||
banner.preview.base64,
|
|
||||||
banner.sizes.small.avif.path.replace(/_small\.avif$/, ''),
|
|
||||||
JSON.stringify(banner.sizes),
|
|
||||||
0,
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
params = params.concat([
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
])
|
|
||||||
}
|
|
||||||
if (media) {
|
|
||||||
params = params.concat([
|
|
||||||
media.filename,
|
|
||||||
media.type,
|
|
||||||
media.path,
|
|
||||||
media.size,
|
|
||||||
media.preview.base64,
|
|
||||||
media.sizes.small.avif.path.replace(/_small\.avif$/, ''),
|
|
||||||
JSON.stringify(media.sizes),
|
|
||||||
0,
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
params = params.concat([
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
console.log(params)
|
console.log(params)
|
||||||
let res = await ctx.db.safeCallProc('common.article_auth_get_update_create', params)
|
let res = await ctx.db.safeCallProc('article_auth_get_update_create', params)
|
||||||
|
|
||||||
let out = {
|
let out = {
|
||||||
article: parseArticle(res.results[0][0]),
|
article: parseArticle(res.results[0][0]) || { publish_at: new Date() },
|
||||||
files: parseFiles(res.results[1]),
|
files: parseFiles(res.results[1]),
|
||||||
staff: res.results[2],
|
staff: res.results[2],
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out.article) {
|
|
||||||
if (out.article.content[0] === '{') {
|
|
||||||
try {
|
|
||||||
out.article.content = JSON.parse(out.article.content)
|
|
||||||
} catch (err) {
|
|
||||||
out.article.content = {
|
|
||||||
time: new Date().getTime(),
|
|
||||||
blocks: [
|
|
||||||
{id: '1', type: 'paragraph', data: { text: 'Error parsing article content: ' + err.message }},
|
|
||||||
],
|
|
||||||
version: '2.25.0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (out.article.content) {
|
|
||||||
out.article.content = {
|
|
||||||
time: new Date().getTime(),
|
|
||||||
blocks: [
|
|
||||||
{id: '1', type: 'htmlraw', data: { html: out.article.content }},
|
|
||||||
],
|
|
||||||
version: '2.25.0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.article = {
|
|
||||||
publish_at: new Date()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.body = out
|
ctx.body = out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** GET: /api/auth/articles/:path */
|
/** GET: /api/auth/articles/:id */
|
||||||
auth_getSingleArticle(ctx) {
|
auth_getSingleArticle(ctx) {
|
||||||
return this.private_getUpdateArticle(ctx)
|
return this.private_getUpdateArticle(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** PUT: /api/auth/articles/:path */
|
/** PUT: /api/auth/articles/:id */
|
||||||
async auth_updateCreateSingleArticle(ctx) {
|
async auth_updateCreateSingleArticle(ctx) {
|
||||||
console.log(ctx.req.body)
|
console.log(ctx.req.body)
|
||||||
|
|
||||||
|
@ -173,11 +102,11 @@ export default class ArticleRoutes {
|
||||||
return this.private_getUpdateArticle(ctx, ctx.req.body, newBanner, newMedia)
|
return this.private_getUpdateArticle(ctx, ctx.req.body, newBanner, newMedia)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** DELETE: /api/auth/articles/:path */
|
/** DELETE: /api/auth/articles/:id */
|
||||||
async auth_removeSingleArticle(ctx) {
|
async auth_removeSingleArticle(ctx) {
|
||||||
let params = [
|
let params = [
|
||||||
ctx.state.auth_token,
|
ctx.state.auth_token,
|
||||||
ctx.params.path,
|
ctx.params.id,
|
||||||
// Article data
|
// Article data
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
@ -187,25 +116,9 @@ export default class ArticleRoutes {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
1,
|
1,
|
||||||
// Banner data
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
1,
|
|
||||||
// Media data
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
1,
|
|
||||||
]
|
]
|
||||||
|
params = params.concat(mediaToDatabase(null, true))
|
||||||
|
params = params.concat(mediaToDatabase(null, true))
|
||||||
|
|
||||||
await ctx.db.safeCallProc('article_auth_get_update_create', params)
|
await ctx.db.safeCallProc('article_auth_get_update_create', params)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { parseFile } from '../file/util.mjs'
|
||||||
|
|
||||||
export function parseArticles(articles) {
|
export function parseArticles(articles) {
|
||||||
for (let i = 0; i < articles.length; i++) {
|
for (let i = 0; i < articles.length; i++) {
|
||||||
parseArticle(articles[i])
|
parseArticle(articles[i])
|
||||||
|
@ -5,17 +7,52 @@ export function parseArticles(articles) {
|
||||||
return articles
|
return articles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function combineFilesWithArticles(articles, files) {
|
||||||
|
let articleMap = new Map()
|
||||||
|
|
||||||
|
articles.forEach(article => {
|
||||||
|
article.files = []
|
||||||
|
articleMap.set(article.id, article)
|
||||||
|
})
|
||||||
|
files.forEach(file => {
|
||||||
|
articleMap.get(file.id).files.push(parseFile(file))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function parseArticle(article) {
|
export function parseArticle(article) {
|
||||||
if (!article) {
|
if (!article) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
if (article.content) {
|
||||||
|
if (article.content[0] === '{') {
|
||||||
|
try {
|
||||||
|
article.content = JSON.parse(article.content)
|
||||||
|
} catch (err) {
|
||||||
|
article.content = {
|
||||||
|
time: new Date().getTime(),
|
||||||
|
blocks: [
|
||||||
|
{id: '1', type: 'paragraph', data: { text: 'Error parsing article content: ' + err.message }},
|
||||||
|
],
|
||||||
|
version: '2.25.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
article.content = {
|
||||||
|
time: new Date().getTime(),
|
||||||
|
blocks: [
|
||||||
|
{id: '1', type: 'htmlraw', data: { html: article.content }},
|
||||||
|
],
|
||||||
|
version: '2.25.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (article.banner_path) {
|
if (article.banner_path) {
|
||||||
article.banner_path = 'https://cdn.nfp.is' + article.banner_path
|
article.banner_path = 'https://cdn.nfp.is' + article.banner_path
|
||||||
article.banner_prefix = 'https://cdn.nfp.is' + article.banner_prefix
|
article.banner_alt_prefix = 'https://cdn.nfp.is' + article.banner_alt_prefix
|
||||||
}
|
}
|
||||||
if (article.media_path) {
|
if (article.media_path) {
|
||||||
article.media_path = 'https://cdn.nfp.is' + article.media_path
|
article.media_path = 'https://cdn.nfp.is' + article.media_path
|
||||||
article.media_prefix = 'https://cdn.nfp.is' + article.media_prefix
|
article.media_alt_prefix = 'https://cdn.nfp.is' + article.media_alt_prefix
|
||||||
}
|
}
|
||||||
return article
|
return article
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ export default class AuthenticationRoutes {
|
||||||
|
|
||||||
/** GET: /api/authentication/login */
|
/** GET: /api/authentication/login */
|
||||||
async login(ctx) {
|
async login(ctx) {
|
||||||
let res = await ctx.db.safeCallProc('common.auth_login', [
|
let res = await ctx.db.safeCallProc('auth_login', [
|
||||||
ctx.req.body.email,
|
ctx.req.body.email,
|
||||||
ctx.req.body.password,
|
ctx.req.body.password,
|
||||||
])
|
])
|
||||||
|
|
|
@ -43,7 +43,7 @@ export function initPool(core, config) {
|
||||||
return {
|
return {
|
||||||
safeCallProc: function(name, params, options) {
|
safeCallProc: function(name, params, options) {
|
||||||
if (name.indexOf('.') < 0) {
|
if (name.indexOf('.') < 0) {
|
||||||
name = config.schema + '.' + name
|
name = 'common.' + name
|
||||||
}
|
}
|
||||||
return pool.promises.callProc(name, params, options)
|
return pool.promises.callProc(name, params, options)
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
|
|
25
api/media/util.mjs
Normal file
25
api/media/util.mjs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
export function mediaToDatabase(media, removeFlag) {
|
||||||
|
if (media) {
|
||||||
|
return [
|
||||||
|
media.filename,
|
||||||
|
media.type,
|
||||||
|
media.path,
|
||||||
|
media.size,
|
||||||
|
media.preview.base64,
|
||||||
|
media.sizes.small.avif.path.replace(/_small\.avif$/, ''),
|
||||||
|
JSON.stringify(media.sizes),
|
||||||
|
removeFlag ? 1 : 0,
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
removeFlag ? 1 : 0,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,48 +0,0 @@
|
||||||
import { parseFile } from '../file/util.mjs'
|
|
||||||
import { parseArticle, parseArticles } from '../article/util.mjs'
|
|
||||||
|
|
||||||
export async function getTree(ctx) {
|
|
||||||
let res = await ctx.db.safeCallProc('common.pages_get_tree', [])
|
|
||||||
let out = []
|
|
||||||
let children = []
|
|
||||||
let map = new Map()
|
|
||||||
for (let page of res.first) {
|
|
||||||
if (!page.parent_id) {
|
|
||||||
out.push(page)
|
|
||||||
} else {
|
|
||||||
children.push(page)
|
|
||||||
}
|
|
||||||
map.set(page.id, page)
|
|
||||||
}
|
|
||||||
for (let page of children) {
|
|
||||||
let parent = map.get(page.parent_id)
|
|
||||||
if (!parent.children) {
|
|
||||||
parent.children = []
|
|
||||||
}
|
|
||||||
parent.children.push(page)
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
tree: out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getPage(ctx, path, page = 0, per_page = 10) {
|
|
||||||
let res = await ctx.db.safeCallProc('common.pages_get_single', [path, page, per_page])
|
|
||||||
|
|
||||||
let articleMap = new Map()
|
|
||||||
|
|
||||||
let out = {
|
|
||||||
page: res.results[0][0] || null,
|
|
||||||
articles: parseArticles(res.results[1]),
|
|
||||||
total_articles: res.results[2][0].total_articles,
|
|
||||||
featured: parseArticle(res.results[4][0]),
|
|
||||||
}
|
|
||||||
out.articles.forEach(article => {
|
|
||||||
article.files = []
|
|
||||||
articleMap.set(article.id, article)
|
|
||||||
})
|
|
||||||
res.results[3].forEach(file => {
|
|
||||||
articleMap.get(file.id).files.push(parseFile(file))
|
|
||||||
})
|
|
||||||
return out
|
|
||||||
}
|
|
|
@ -1,26 +1,131 @@
|
||||||
import * as Page from './model.mjs'
|
import { parsePagesToTree } from './util.mjs'
|
||||||
import * as security from './security.mjs'
|
import { upload } from '../media/upload.mjs'
|
||||||
|
import { combineFilesWithArticles, parseArticle, parseArticles } from '../article/util.mjs'
|
||||||
|
import { mediaToDatabase } from '../media/util.mjs'
|
||||||
|
|
||||||
|
|
||||||
export default class PageRoutes {
|
export default class PageRoutes {
|
||||||
constructor(opts = {}) {
|
constructor(opts = {}) {
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
Page: opts.Page || Page,
|
upload: upload,
|
||||||
security: opts.security || security,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GET: /api/pagetree */
|
/** GET: /api/pagetree */
|
||||||
async getPageTree(ctx) {
|
async getPageTree(ctx, onlyReturn = false) {
|
||||||
ctx.body = await this.Page.getTree(ctx)
|
let res = await ctx.db.safeCallProc('pages_get_tree', [])
|
||||||
|
|
||||||
|
if (onlyReturn) {
|
||||||
|
return parsePagesToTree(res.first)
|
||||||
|
}
|
||||||
|
ctx.body = parsePagesToTree(res.first)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GET: /api/pages/[path] */
|
/** GET: /api/pages/[path] */
|
||||||
async getPage(ctx) {
|
async getPage(ctx) {
|
||||||
ctx.body = await this.Page.getPage(
|
let res = await ctx.db.safeCallProc('pages_get_single', [
|
||||||
ctx,
|
|
||||||
ctx.params.path || null,
|
ctx.params.path || null,
|
||||||
Math.max(ctx.query.get('page') || 1, 1),
|
Math.max(ctx.query.get('page') || 1, 1),
|
||||||
Math.min(ctx.query.get('per_page') || 10, 25)
|
Math.min(ctx.query.get('per_page') || 10, 25),
|
||||||
|
])
|
||||||
|
|
||||||
|
let out = {
|
||||||
|
page: res.results[0][0] || null,
|
||||||
|
articles: parseArticles(res.results[1]),
|
||||||
|
total_articles: res.results[2][0].total_articles,
|
||||||
|
featured: parseArticle(res.results[4][0]),
|
||||||
|
}
|
||||||
|
|
||||||
|
combineFilesWithArticles(out.articles, res.results[3])
|
||||||
|
|
||||||
|
ctx.body = out
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GET: /api/auth/pages */
|
||||||
|
async auth_getAllPages(ctx) {
|
||||||
|
let res = await ctx.db.safeCallProc('pages_auth_get_all', [
|
||||||
|
ctx.state.auth_token
|
||||||
|
])
|
||||||
|
|
||||||
|
ctx.body = parsePagesToTree(res.first)
|
||||||
|
}
|
||||||
|
|
||||||
|
async private_getUpdatePage(ctx, body = null, banner = null, media = null) {
|
||||||
|
let params = [
|
||||||
|
ctx.state.auth_token,
|
||||||
|
ctx.params.id === '0' ? null : ctx.params.id
|
||||||
|
]
|
||||||
|
if (body) {
|
||||||
|
params = params.concat([
|
||||||
|
body.name,
|
||||||
|
body.parent_id === 'null' ? null : Number(body.parent_id),
|
||||||
|
body.path,
|
||||||
|
body.content,
|
||||||
|
0,
|
||||||
|
])
|
||||||
|
params = params.concat(mediaToDatabase(banner, body.remove_banner === 'true'))
|
||||||
|
params = params.concat(mediaToDatabase(media, body.remove_media === 'true'))
|
||||||
|
}
|
||||||
|
console.log(params)
|
||||||
|
let res = await ctx.db.safeCallProc('pages_auth_get_update_create', params)
|
||||||
|
|
||||||
|
let out = {
|
||||||
|
page: res.results[0][0] || {},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.body = out
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** GET: /api/auth/pages/:id */
|
||||||
|
auth_getSinglePage(ctx) {
|
||||||
|
return this.private_getUpdatePage(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** PUT: /api/auth/pages/:id */
|
||||||
|
async auth_updateCreateSinglePage(ctx) {
|
||||||
|
console.log(ctx.req.body)
|
||||||
|
|
||||||
|
let newBanner = null
|
||||||
|
let newMedia = null
|
||||||
|
|
||||||
|
let promises = []
|
||||||
|
|
||||||
|
if (ctx.req.files.banner) {
|
||||||
|
promises.push(
|
||||||
|
this.upload(ctx.req.files.banner)
|
||||||
|
.then(res => { newBanner = res })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (ctx.req.files.media) {
|
||||||
|
promises.push(
|
||||||
|
this.upload(ctx.req.files.media)
|
||||||
|
.then(res => { newMedia = res })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
|
||||||
|
return this.private_getUpdatePage(ctx, ctx.req.body, newBanner, newMedia)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** DELETE: /api/auth/pages/:id */
|
||||||
|
async auth_removeSinglePage(ctx) {
|
||||||
|
let params = [
|
||||||
|
ctx.state.auth_token,
|
||||||
|
ctx.params.id,
|
||||||
|
// Page data
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
]
|
||||||
|
params = params.concat(mediaToDatabase(null, true))
|
||||||
|
params = params.concat(mediaToDatabase(null, true))
|
||||||
|
|
||||||
|
await ctx.db.safeCallProc('pages_auth_get_update_create', params)
|
||||||
|
|
||||||
|
ctx.status = 204
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
23
api/page/util.mjs
Normal file
23
api/page/util.mjs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
export function parsePagesToTree(pages) {
|
||||||
|
let out = []
|
||||||
|
let children = []
|
||||||
|
let map = new Map()
|
||||||
|
for (let page of pages) {
|
||||||
|
if (!page.parent_id) {
|
||||||
|
out.push(page)
|
||||||
|
} else {
|
||||||
|
children.push(page)
|
||||||
|
}
|
||||||
|
map.set(page.id, page)
|
||||||
|
}
|
||||||
|
for (let page of children) {
|
||||||
|
let parent = map.get(page.parent_id)
|
||||||
|
if (!parent.children) {
|
||||||
|
parent.children = []
|
||||||
|
}
|
||||||
|
parent.children.push(page)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
tree: out
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,10 @@ import { FileResponse, HttpError } from 'flaska'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import fsSync from 'fs'
|
import fsSync from 'fs'
|
||||||
|
|
||||||
import { getTree } from './page/model.mjs'
|
|
||||||
|
|
||||||
export default class ServeHandler {
|
export default class ServeHandler {
|
||||||
constructor(opts = {}) {
|
constructor(opts = {}) {
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
|
pageRoutes: opts.pageRoutes,
|
||||||
fs: opts.fs || fs,
|
fs: opts.fs || fs,
|
||||||
fsSync: opts.fsSync || fsSync,
|
fsSync: opts.fsSync || fsSync,
|
||||||
root: opts.root,
|
root: opts.root,
|
||||||
|
@ -66,7 +65,7 @@ export default class ServeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
payload.payloadTree = JSON.stringify(await getTree(ctx))
|
payload.payloadTree = JSON.stringify(await this.pageRoutes.getPageTree(ctx, true))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.error(e)
|
ctx.log.error(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,19 +60,30 @@ export function run(http, port, core) {
|
||||||
flaska.get('/api/pagetree', page.getPageTree.bind(page))
|
flaska.get('/api/pagetree', page.getPageTree.bind(page))
|
||||||
flaska.get('/api/frontpage', page.getPage.bind(page))
|
flaska.get('/api/frontpage', page.getPage.bind(page))
|
||||||
flaska.get('/api/pages/:path', page.getPage.bind(page))
|
flaska.get('/api/pages/:path', page.getPage.bind(page))
|
||||||
// flaska.get('/api/pages/:pageId', page.getSinglePage.bind(page))
|
flaska.get('/api/auth/pages', authenticate(), page.auth_getAllPages.bind(page))
|
||||||
|
flaska.get('/api/auth/pages/:id', authenticate(), page.auth_getSinglePage.bind(page))
|
||||||
|
flaska.put('/api/auth/pages/:id', [
|
||||||
|
authenticate(),
|
||||||
|
FormidableHandler(formidable, { maxFileSize: 20 * 1024 * 1024, }),
|
||||||
|
], page.auth_updateCreateSinglePage.bind(page))
|
||||||
|
flaska.delete('/api/auth/pages/:id', authenticate(), page.auth_removeSinglePage.bind(page))
|
||||||
|
|
||||||
|
|
||||||
const article = new ArticleRoutes()
|
const article = new ArticleRoutes()
|
||||||
flaska.get('/api/articles/:path', article.getArticle.bind(article))
|
flaska.get('/api/articles/:path', article.getArticle.bind(article))
|
||||||
flaska.get('/api/auth/articles', authenticate(), article.auth_getAllArticles.bind(article))
|
flaska.get('/api/auth/articles', authenticate(), article.auth_getAllArticles.bind(article))
|
||||||
flaska.get('/api/auth/articles/:path', authenticate(), article.auth_getSingleArticle.bind(article))
|
flaska.get('/api/auth/articles/:id', authenticate(), article.auth_getSingleArticle.bind(article))
|
||||||
flaska.put('/api/auth/articles/:path', [authenticate(), FormidableHandler(formidable) ], article.auth_updateCreateSingleArticle.bind(article))
|
flaska.put('/api/auth/articles/:id', [
|
||||||
// flaska.get('/api/pages/:pageId/articles/public', article.getPublicAllPageArticles.bind(article))
|
authenticate(),
|
||||||
|
FormidableHandler(formidable, { maxFileSize: 20 * 1024 * 1024, }),
|
||||||
|
], article.auth_updateCreateSingleArticle.bind(article))
|
||||||
|
flaska.delete('/api/auth/articles/:id', authenticate(), article.auth_removeSingleArticle.bind(article))
|
||||||
|
|
||||||
const authentication = new AuthenticationRoutes()
|
const authentication = new AuthenticationRoutes()
|
||||||
flaska.post('/api/authentication/login', JsonHandler(), authentication.login.bind(authentication))
|
flaska.post('/api/authentication/login', JsonHandler(), authentication.login.bind(authentication))
|
||||||
|
|
||||||
const serve = new ServeHandler({
|
const serve = new ServeHandler({
|
||||||
|
pageRoutes: page,
|
||||||
root: localUtil.getPathFromRoot('../public'),
|
root: localUtil.getPathFromRoot('../public'),
|
||||||
version: core.app.running,
|
version: core.app.running,
|
||||||
frontend: config.get('frontend:url'),
|
frontend: config.get('frontend:url'),
|
||||||
|
|
|
@ -1,72 +1,3 @@
|
||||||
:root {
|
|
||||||
--primary-bg: #01579b;
|
|
||||||
--primary-fg: white;
|
|
||||||
--primary-fg-url: #FFC7C7;
|
|
||||||
--primary-light-bg: #3D77C7; // #4f83cc;
|
|
||||||
--primary-light-fg: white;
|
|
||||||
--primary-dark-bg: #002f6c;
|
|
||||||
--primary-dark-fg: white;
|
|
||||||
|
|
||||||
--secondary-bg: #f57c00;
|
|
||||||
--secondary-fg: black;
|
|
||||||
--secondary-light-bg: #ffad42;
|
|
||||||
--secondary-light-fg: black;
|
|
||||||
--secondary-dark-bg: #bb4d00;
|
|
||||||
--secondary-dark-fg: white;
|
|
||||||
|
|
||||||
--table-fg: #333;
|
|
||||||
--border: #ccc;
|
|
||||||
--border-fg: black;
|
|
||||||
--border-fg-url: #8f3c00;
|
|
||||||
--title-fg: #555;
|
|
||||||
--meta-fg: #757575; // #999
|
|
||||||
--meta-light-fg: #999999;
|
|
||||||
|
|
||||||
--main-bg: white;
|
|
||||||
--main-fg: black;
|
|
||||||
--input-bg: white;
|
|
||||||
--input-border: #333;
|
|
||||||
--input-fg: black;
|
|
||||||
|
|
||||||
--newsitem-bg: transparent;
|
|
||||||
--newsitem-border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkmodeon {
|
|
||||||
--primary-bg: #013b68;
|
|
||||||
--primary-fg: white;
|
|
||||||
--primary-fg-url: #FFC7C7;
|
|
||||||
--primary-light-bg: #28518B;
|
|
||||||
--primary-light-fg: white;
|
|
||||||
--primary-dark-bg: #002f6c;
|
|
||||||
--primary-dark-fg: white;
|
|
||||||
|
|
||||||
--secondary-bg: #e05e00;
|
|
||||||
--secondary-fg: black;
|
|
||||||
--secondary-light-bg: #ffad42;
|
|
||||||
--secondary-light-fg: black;
|
|
||||||
--secondary-dark-bg: #e05e00;
|
|
||||||
--secondary-dark-fg: white;
|
|
||||||
--secondary-darker-fg: #fe791b;
|
|
||||||
|
|
||||||
--table-fg: #333;
|
|
||||||
--border: #343536;
|
|
||||||
--border-fg: #d7dadc;;
|
|
||||||
--border-fg-url: #e05e00;
|
|
||||||
--title-fg: #808080;
|
|
||||||
--meta-fg: hsl(0, 0%, 55%);
|
|
||||||
--meta-light-fg: #999999;
|
|
||||||
|
|
||||||
--main-bg: black;
|
|
||||||
--main-fg: #d7dadc;
|
|
||||||
--input-bg: #272729;
|
|
||||||
--input-border: #343536;
|
|
||||||
--input-fg: white;
|
|
||||||
|
|
||||||
--newsitem-bg: #1a1a1b;
|
|
||||||
--newsitem-border: 1px solid #343536;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$primary-bg: #01579b;
|
$primary-bg: #01579b;
|
||||||
$primary-fg: white;
|
$primary-fg: white;
|
||||||
|
|
|
@ -52,7 +52,6 @@ const AdminArticles = {
|
||||||
url: '/api/auth/articles?page=' + (this.lastpage || 1),
|
url: '/api/auth/articles?page=' + (this.lastpage || 1),
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
console.log(result)
|
|
||||||
this.data = result
|
this.data = result
|
||||||
|
|
||||||
this.data.articles.forEach((article) => {
|
this.data.articles.forEach((article) => {
|
||||||
|
@ -75,13 +74,20 @@ const AdminArticles = {
|
||||||
let removingArticle = this.removeArticle
|
let removingArticle = this.removeArticle
|
||||||
this.removeArticle = null
|
this.removeArticle = null
|
||||||
this.loading = true
|
this.loading = true
|
||||||
Article.removeArticle(removingArticle, removingArticle.id)
|
|
||||||
.then(this.oninit.bind(this, vnode))
|
|
||||||
.catch(function(err) {
|
|
||||||
vnode.state.error = err.message
|
|
||||||
vnode.state.loading = false
|
|
||||||
m.redraw()
|
m.redraw()
|
||||||
|
|
||||||
|
return common.sendRequest({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/api/auth/articles/' + removingArticle.id,
|
||||||
})
|
})
|
||||||
|
.then(
|
||||||
|
() => this.fetchArticles(vnode),
|
||||||
|
(err) => {
|
||||||
|
this.error = err.message
|
||||||
|
this.loading = false
|
||||||
|
m.redraw()
|
||||||
|
}
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
drawArticle: function(vnode, article) {
|
drawArticle: function(vnode, article) {
|
||||||
|
@ -93,7 +99,7 @@ const AdminArticles = {
|
||||||
? 'rowfeatured'
|
? 'rowfeatured'
|
||||||
: ''
|
: ''
|
||||||
}, [
|
}, [
|
||||||
m('td', m(m.route.Link, { href: '/admin/articles/' + article.path }, article.name)),
|
m('td', m(m.route.Link, { href: '/admin/articles/' + article.id }, article.name)),
|
||||||
m('td', m(m.route.Link, { href: article.page_path }, article.page_name)),
|
m('td', m(m.route.Link, { href: article.page_path }, article.page_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.publish_at.replace('T', ' ').split('.')[0]),
|
m('td.right', article.publish_at.replace('T', ' ').split('.')[0]),
|
||||||
|
|
|
@ -16,7 +16,7 @@ const EditArticle = {
|
||||||
staff: [],
|
staff: [],
|
||||||
}
|
}
|
||||||
this.pages = [{id: null, name: 'Frontpage'}]
|
this.pages = [{id: null, name: 'Frontpage'}]
|
||||||
this.addPageTree('', Page.Tree)
|
this.pages = this.pages.concat(Page.getFlatTree())
|
||||||
this.newBanner = null
|
this.newBanner = null
|
||||||
this.newMedia = null
|
this.newMedia = null
|
||||||
this.dateInstance = null
|
this.dateInstance = null
|
||||||
|
@ -25,15 +25,6 @@ const EditArticle = {
|
||||||
this.fetchArticle(vnode)
|
this.fetchArticle(vnode)
|
||||||
},
|
},
|
||||||
|
|
||||||
addPageTree: function(prefix, branches) {
|
|
||||||
branches.forEach((page) => {
|
|
||||||
this.pages.push({ id: page.id, name: prefix + page.name })
|
|
||||||
if (page.children && page.children.length) {
|
|
||||||
this.addPageTree(page.name + ' -> ', page.children)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
onbeforeupdate: function(vnode) {
|
onbeforeupdate: function(vnode) {
|
||||||
if (this.lastid !== m.route.param('id')) {
|
if (this.lastid !== m.route.param('id')) {
|
||||||
this.fetchArticle(vnode)
|
this.fetchArticle(vnode)
|
||||||
|
@ -42,16 +33,11 @@ const EditArticle = {
|
||||||
|
|
||||||
fetchArticle: function(vnode) {
|
fetchArticle: function(vnode) {
|
||||||
this.lastid = m.route.param('id')
|
this.lastid = m.route.param('id')
|
||||||
let id = this.lastid
|
|
||||||
if (id === 'add') {
|
|
||||||
id = '0'
|
|
||||||
}
|
|
||||||
this.error = ''
|
|
||||||
|
|
||||||
return this.requestArticle(
|
return this.requestArticle(
|
||||||
common.sendRequest({
|
common.sendRequest({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/api/auth/articles/' + id,
|
url: '/api/auth/articles/' + (this.lastid === 'add' ? '0' : this.lastid),
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -75,9 +61,11 @@ const EditArticle = {
|
||||||
data
|
data
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
this.data = result
|
this.data = result
|
||||||
if (this.data.article) {
|
|
||||||
this.data.article.publish_at = new Date(this.data.article.publish_at)
|
this.data.article.publish_at = new Date(this.data.article.publish_at)
|
||||||
|
|
||||||
|
if (this.data.article.id) {
|
||||||
document.title = 'Editing: ' + this.data.article.name + ' - Admin NFP Moe'
|
document.title = 'Editing: ' + this.data.article.name + ' - Admin NFP Moe'
|
||||||
|
this.editedPath = true
|
||||||
} else {
|
} else {
|
||||||
document.title = 'Create Article - Admin NFP Moe'
|
document.title = 'Create Article - Admin NFP Moe'
|
||||||
}
|
}
|
||||||
|
@ -122,16 +110,20 @@ const EditArticle = {
|
||||||
},
|
},
|
||||||
|
|
||||||
mediaRemoved: function(type) {
|
mediaRemoved: function(type) {
|
||||||
this.data.article[type] = null
|
this.data.article['remove_' + type] = true
|
||||||
|
this.data.article[type + '_prefix'] = null
|
||||||
},
|
},
|
||||||
|
|
||||||
save: function(vnode, e) {
|
save: function(vnode, e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
if (!this.data.article.name) {
|
||||||
let id = this.lastid
|
this.error = 'Name is missing'
|
||||||
if (id === 'add') {
|
} else if (!this.data.article.path) {
|
||||||
id = '0'
|
this.error = 'Path is missing'
|
||||||
|
} else {
|
||||||
|
this.error = ''
|
||||||
}
|
}
|
||||||
|
if (this.error) return
|
||||||
|
|
||||||
let formData = new FormData()
|
let formData = new FormData()
|
||||||
if (this.newBanner) {
|
if (this.newBanner) {
|
||||||
|
@ -150,6 +142,8 @@ const EditArticle = {
|
||||||
formData.append('path', this.data.article.path)
|
formData.append('path', this.data.article.path)
|
||||||
formData.append('page_id', this.data.article.page_id || null)
|
formData.append('page_id', this.data.article.page_id || null)
|
||||||
formData.append('publish_at', this.dateInstance.inputElem.value.replace(', ', 'T') + 'Z')
|
formData.append('publish_at', this.dateInstance.inputElem.value.replace(', ', 'T') + 'Z')
|
||||||
|
formData.append('remove_banner', this.data.article.remove_banner ? true : false)
|
||||||
|
formData.append('remove_media', this.data.article.remove_media ? true : false)
|
||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
|
@ -160,10 +154,19 @@ const EditArticle = {
|
||||||
|
|
||||||
return common.sendRequest({
|
return common.sendRequest({
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
url: '/api/auth/articles/' + id,
|
url: '/api/auth/articles/' + (this.lastid === 'add' ? '0' : this.lastid),
|
||||||
body: formData,
|
body: formData,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (!data.article.id) {
|
||||||
|
throw new Error('Something went wrong with saving, try again later')
|
||||||
|
} else if (this.lastid === 'add') {
|
||||||
|
this.lastid = data.article.id.toString()
|
||||||
|
m.route.set('/admin/articles/' + data.article.id)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
})
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -198,6 +201,10 @@ const EditArticle = {
|
||||||
]
|
]
|
||||||
: null),
|
: null),
|
||||||
m('article.editarticle', [
|
m('article.editarticle', [
|
||||||
|
m('header', m('h1',
|
||||||
|
(this.data.article.id ? 'Edit ' : 'Create Article ') + (this.data.article.name || '(untitled)')
|
||||||
|
)
|
||||||
|
),
|
||||||
m('header', m('h1', this.creating ? 'Create Article' : 'Edit ' + (this.data.article.name || '(untitled)'))),
|
m('header', m('h1', this.creating ? 'Create Article' : 'Edit ' + (this.data.article.name || '(untitled)'))),
|
||||||
m('div.error', {
|
m('div.error', {
|
||||||
hidden: !this.error,
|
hidden: !this.error,
|
||||||
|
@ -279,7 +286,7 @@ const EditArticle = {
|
||||||
this.data.staff.map((item) => {
|
this.data.staff.map((item) => {
|
||||||
return m('option', {
|
return m('option', {
|
||||||
value: item.id,
|
value: item.id,
|
||||||
selected: item.id === this.data.article.staff_id
|
selected: item.id === this.data.article.admin_id
|
||||||
}, item.name)
|
}, item.name)
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
@ -293,7 +300,9 @@ const EditArticle = {
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
m('div', [
|
m('div', {
|
||||||
|
hidden: !this.data.article.name || !this.data.article.path
|
||||||
|
}, [
|
||||||
m('input', {
|
m('input', {
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
value: 'Save',
|
value: 'Save',
|
||||||
|
|
|
@ -1,245 +1,261 @@
|
||||||
const Authentication = require('../authentication')
|
|
||||||
const FileUpload = require('../widgets/fileupload')
|
const FileUpload = require('../widgets/fileupload')
|
||||||
const Froala = require('./froala')
|
const Page = require('../api/page.p')
|
||||||
const Page = require('../api/page')
|
|
||||||
|
const common = require('../api/common')
|
||||||
|
const Editor = require('./editor')
|
||||||
|
|
||||||
const EditPage = {
|
const EditPage = {
|
||||||
getFroalaOptions: function() {
|
|
||||||
return {
|
|
||||||
theme: 'gray',
|
|
||||||
heightMin: 150,
|
|
||||||
videoUpload: false,
|
|
||||||
imageUploadURL: '/api/media',
|
|
||||||
imageManagerLoadURL: '/api/media',
|
|
||||||
imageManagerDeleteMethod: 'DELETE',
|
|
||||||
imageManagerDeleteURL: '/api/media',
|
|
||||||
events: {
|
|
||||||
'imageManager.beforeDeleteImage': function(img) {
|
|
||||||
this.opts.imageManagerDeleteURL = '/api/media/' + img.data('id')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
requestHeaders: {
|
|
||||||
'Authorization': 'Bearer ' + Authentication.getToken(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
oninit: function(vnode) {
|
oninit: function(vnode) {
|
||||||
this.froala = null
|
this.loading = false
|
||||||
this.loadedFroala = Froala.loadedFroala
|
this.showLoading = null
|
||||||
|
this.data = {
|
||||||
if (!this.loadedFroala) {
|
page: null,
|
||||||
Froala.createFroalaScript()
|
|
||||||
.then(function() {
|
|
||||||
vnode.state.loadedFroala = true
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
this.pages = [{id: null, name: 'Frontpage'}]
|
||||||
|
this.pages = this.pages.concat(Page.getFlatTree())
|
||||||
|
|
||||||
|
this.newBanner = null
|
||||||
|
this.newMedia = null
|
||||||
|
this.editor = null
|
||||||
|
|
||||||
this.fetchPage(vnode)
|
this.fetchPage(vnode)
|
||||||
},
|
},
|
||||||
|
|
||||||
onupdate: function(vnode) {
|
onbeforeupdate: function(vnode) {
|
||||||
if (this.lastid !== m.route.param('id')) {
|
if (this.lastid !== m.route.param('id')) {
|
||||||
this.fetchPage(vnode)
|
this.fetchPage(vnode)
|
||||||
if (this.lastid === 'add') {
|
|
||||||
m.redraw()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchPage: function(vnode) {
|
fetchPage: function(vnode) {
|
||||||
this.lastid = m.route.param('id')
|
this.lastid = m.route.param('id')
|
||||||
this.loading = this.lastid !== 'add'
|
|
||||||
this.creating = this.lastid === 'add'
|
|
||||||
this.error = ''
|
|
||||||
this.page = {
|
|
||||||
name: '',
|
|
||||||
path: '',
|
|
||||||
description: '',
|
|
||||||
media: null,
|
|
||||||
}
|
|
||||||
this.editedPath = false
|
|
||||||
|
|
||||||
if (this.lastid !== 'add') {
|
return this.requestPage(
|
||||||
Page.getPage(this.lastid)
|
common.sendRequest({
|
||||||
.then(function(result) {
|
method: 'GET',
|
||||||
vnode.state.editedPath = true
|
url: '/api/auth/pages/' + (this.lastid === 'add' ? '0' : this.lastid),
|
||||||
vnode.state.page = result
|
}))
|
||||||
document.title = 'Editing: ' + result.name + ' - Admin NFP Moe'
|
},
|
||||||
})
|
|
||||||
.catch(function(err) {
|
requestPage: function(data) {
|
||||||
vnode.state.error = err.message
|
this.error = ''
|
||||||
})
|
|
||||||
.then(function() {
|
if (this.showLoading) {
|
||||||
vnode.state.loading = false
|
clearTimeout(this.showLoading)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.data.page) {
|
||||||
|
this.showLoading = setTimeout(() => {
|
||||||
|
this.showLoading = null
|
||||||
|
this.loading = true
|
||||||
m.redraw()
|
m.redraw()
|
||||||
})
|
}, 150)
|
||||||
|
} else {
|
||||||
|
this.loading = true
|
||||||
|
}
|
||||||
|
|
||||||
|
data
|
||||||
|
.then((result) => {
|
||||||
|
this.data = result
|
||||||
|
if (this.data.page.id) {
|
||||||
|
document.title = 'Editing: ' + this.data.page.name + ' - Admin NFP Moe'
|
||||||
|
this.editedPath = true
|
||||||
} else {
|
} else {
|
||||||
document.title = 'Create Page - Admin NFP Moe'
|
document.title = 'Create Page - Admin NFP Moe'
|
||||||
}
|
}
|
||||||
|
}, (err) => {
|
||||||
|
this.error = err.message
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
clearTimeout(this.showLoading)
|
||||||
|
this.showLoading = null
|
||||||
|
this.loading = false
|
||||||
|
m.redraw()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
updateValue: function(name, e) {
|
updateValue: function(name, e) {
|
||||||
this.page[name] = e.currentTarget.value
|
this.data.page[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) {
|
||||||
this.page.path = this.page.name.toLowerCase().replace(/ /g, '-')
|
this.data.page.path = this.data.page.name.toLowerCase().replace(/ /g, '-')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateParent: function(e) {
|
updateParent: function(e) {
|
||||||
this.page.parent_id = Number(e.currentTarget.value)
|
this.data.page.parent_id = Number(e.currentTarget.value) || null
|
||||||
if (this.page.parent_id === -1) {
|
},
|
||||||
this.page.parent_id = null
|
|
||||||
|
mediaUploaded: function(type, file) {
|
||||||
|
if (type === 'banner') {
|
||||||
|
this.newBanner = file
|
||||||
|
} else {
|
||||||
|
this.newMedia = file
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
fileUploaded: function(type, media) {
|
mediaRemoved: function(type) {
|
||||||
this.page[type] = media
|
this.data.page['remove_' + type] = true
|
||||||
},
|
this.data.page[type + '_prefix'] = null
|
||||||
|
|
||||||
fileRemoved: function(type) {
|
|
||||||
this.page[type] = null
|
|
||||||
},
|
},
|
||||||
|
|
||||||
save: function(vnode, e) {
|
save: function(vnode, e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (!this.page.name) {
|
if (!this.data.page.name) {
|
||||||
this.error = 'Name is missing'
|
this.error = 'Name is missing'
|
||||||
} else if (!this.page.path) {
|
} else if (!this.data.page.path) {
|
||||||
this.error = 'Path is missing'
|
this.error = 'Path is missing'
|
||||||
} else {
|
} else {
|
||||||
this.error = ''
|
this.error = ''
|
||||||
}
|
}
|
||||||
if (this.error) return
|
if (this.error) return
|
||||||
|
|
||||||
this.page.description = vnode.state.froala ? vnode.state.froala.html.get() : this.page.description
|
let formData = new FormData()
|
||||||
if (this.page.description) {
|
if (this.newBanner) {
|
||||||
this.page.description = this.page.description.replace(/<p[^>]+data-f-id="pbf"[^>]+>[^>]+>[^>]+>[^>]+>/, '')
|
formData.append('banner', this.newBanner.file)
|
||||||
}
|
}
|
||||||
|
if (this.newMedia) {
|
||||||
|
formData.append('media', this.newMedia.file)
|
||||||
|
}
|
||||||
|
if (this.data.page.id) {
|
||||||
|
formData.append('id', this.data.page.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
formData.append('name', this.data.page.name)
|
||||||
|
formData.append('parent_id', this.data.page.parent_id || null)
|
||||||
|
formData.append('path', this.data.page.path)
|
||||||
|
formData.append('remove_banner', this.data.page.remove_banner ? true : false)
|
||||||
|
formData.append('remove_media', this.data.page.remove_media ? true : false)
|
||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
let promise
|
this.requestPage(
|
||||||
|
this.editor.save()
|
||||||
|
.then(body => {
|
||||||
|
formData.append('content', JSON.stringify(body))
|
||||||
|
|
||||||
if (this.page.id) {
|
return common.sendRequest({
|
||||||
promise = Page.updatePage(this.page.id, {
|
method: 'PUT',
|
||||||
name: this.page.name,
|
url: '/api/auth/pages/' + (this.lastid === 'add' ? '0' : this.lastid),
|
||||||
path: this.page.path,
|
body: formData,
|
||||||
parent_id: this.page.parent_id,
|
|
||||||
description: this.page.description,
|
|
||||||
banner_id: this.page.banner && this.page.banner.id || null,
|
|
||||||
media_id: this.page.media && this.page.media.id || null,
|
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
promise = Page.createPage({
|
|
||||||
name: this.page.name,
|
|
||||||
path: this.page.path,
|
|
||||||
parent_id: this.page.parent_id,
|
|
||||||
description: this.page.description,
|
|
||||||
banner_id: this.page.banner && this.page.banner.id || null,
|
|
||||||
media_id: this.page.media && this.page.media.id || null,
|
|
||||||
})
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (!data.page.id) {
|
||||||
|
throw new Error('Something went wrong with saving, try again later')
|
||||||
|
} else if (this.lastid === 'add') {
|
||||||
|
this.lastid = data.page.id.toString()
|
||||||
|
m.route.set('/admin/pages/' + data.page.id)
|
||||||
}
|
}
|
||||||
|
return Page.refreshTree().then(() => {
|
||||||
|
this.pages = [{id: null, name: 'Frontpage'}]
|
||||||
|
this.pages = this.pages.concat(Page.getFlatTree())
|
||||||
|
|
||||||
promise.then(function(res) {
|
return data
|
||||||
if (vnode.state.page.id) {
|
|
||||||
res.media = vnode.state.page.media
|
|
||||||
res.banner = vnode.state.page.banner
|
|
||||||
vnode.state.page = res
|
|
||||||
console.log(res)
|
|
||||||
} else {
|
|
||||||
m.route.set('/admin/pages/' + res.id)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(function(err) {
|
|
||||||
vnode.state.error = err.message
|
|
||||||
})
|
})
|
||||||
.then(function() {
|
)
|
||||||
vnode.state.loading = false
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
return false
|
|
||||||
},
|
},
|
||||||
|
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
const parents = [{id: null, name: '-- Frontpage --'}].concat(Page.Tree).filter(function (page) { return !vnode.state.page || page.id !== vnode.state.page.id})
|
const bannerImage = this.data.page && this.data.page.banner_prefix
|
||||||
return (
|
? this.data.page.banner_prefix + '_large.avif'
|
||||||
this.loading ?
|
: null
|
||||||
m('div.loading-spinner')
|
const mediaImage = this.data.page && this.data.page.media_prefix
|
||||||
: m('div.admin-wrapper', [
|
? this.data.page.media_prefix + '_large.avif'
|
||||||
m('div.admin-actions', this.page.id
|
: null
|
||||||
|
|
||||||
|
return [
|
||||||
|
this.loading && !this.data.page
|
||||||
|
? m('div.admin-spinner.loading-spinner')
|
||||||
|
: null,
|
||||||
|
this.data.page
|
||||||
|
? m('div.admin-wrapper', [
|
||||||
|
this.loading
|
||||||
|
? m('div.loading-spinner')
|
||||||
|
: null,
|
||||||
|
m('div.admin-actions', this.data.page.id
|
||||||
? [
|
? [
|
||||||
m('span', 'Actions:'),
|
m('span', 'Actions:'),
|
||||||
m(m.route.Link, { href: '/page/' + this.page.path }, 'View page'),
|
m(m.route.Link, { href: '/page/' + this.data.page.path }, 'View page'),
|
||||||
m(m.route.Link, { href: '/admin/pages/add' }, 'Create new page'),
|
|
||||||
]
|
]
|
||||||
: null),
|
: null),
|
||||||
m('article.editpage', [
|
m('article.editarticle', [
|
||||||
m('header', m('h1', this.creating ? 'Create Page' : 'Edit ' + (this.page.name || '(untitled)'))),
|
m('header', m('h1',
|
||||||
|
(this.data.page.id ? 'Edit ' : 'Create Page ') + (this.data.page.name || '(untitled)')
|
||||||
|
)
|
||||||
|
),
|
||||||
m('div.error', {
|
m('div.error', {
|
||||||
hidden: !this.error,
|
hidden: !this.error,
|
||||||
onclick: function() { vnode.state.error = '' },
|
onclick: () => { vnode.state.error = '' },
|
||||||
}, this.error),
|
}, this.error),
|
||||||
m(FileUpload, {
|
m(FileUpload, {
|
||||||
onupload: this.fileUploaded.bind(this, 'banner'),
|
height: 300,
|
||||||
ondelete: this.fileRemoved.bind(this, 'banner'),
|
onfile: this.mediaUploaded.bind(this, 'banner'),
|
||||||
onerror: function(e) { vnode.state.error = e },
|
ondelete: this.mediaRemoved.bind(this, 'banner'),
|
||||||
media: this.page && this.page.banner,
|
media: bannerImage,
|
||||||
}),
|
}),
|
||||||
m(FileUpload, {
|
m(FileUpload, {
|
||||||
class: 'cover',
|
class: 'cover',
|
||||||
useimg: true,
|
useimg: true,
|
||||||
onupload: this.fileUploaded.bind(this, 'media'),
|
onfile: this.mediaUploaded.bind(this, 'media'),
|
||||||
ondelete: this.fileRemoved.bind(this, 'media'),
|
ondelete: this.mediaRemoved.bind(this, 'media'),
|
||||||
onerror: function(e) { vnode.state.error = e },
|
media: mediaImage,
|
||||||
media: this.page && this.page.media,
|
|
||||||
}),
|
}),
|
||||||
m('form.editpage.content', {
|
m('form.editarticle.content', {
|
||||||
onsubmit: this.save.bind(this, vnode),
|
onsubmit: this.save.bind(this, vnode),
|
||||||
}, [
|
}, [
|
||||||
m('label', 'Parent'),
|
m('label', 'Parent'),
|
||||||
m('select', {
|
m('select', {
|
||||||
onchange: this.updateParent.bind(this),
|
onchange: this.updateParent.bind(this),
|
||||||
}, parents.map(function(item) {
|
}, this.pages.filter(item => !this.data.page || item.id !== this.data.page.id).map((item) => {
|
||||||
return m('option', { value: item.id || -1, selected: item.id === vnode.state.page.parent_id }, item.name)
|
return m('option', {
|
||||||
|
value: item.id || 0,
|
||||||
|
selected: item.id === this.data.page.parent_id
|
||||||
|
}, item.name)
|
||||||
})),
|
})),
|
||||||
|
m('div.input-row', [
|
||||||
|
m('div.input-group', [
|
||||||
m('label', 'Name'),
|
m('label', 'Name'),
|
||||||
m('input', {
|
m('input', {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: this.page.name,
|
value: this.data.page.name,
|
||||||
oninput: this.updateValue.bind(this, 'name'),
|
oninput: this.updateValue.bind(this, 'name'),
|
||||||
}),
|
}),
|
||||||
m('label', 'Description'),
|
]),
|
||||||
(
|
m('div.input-group', [
|
||||||
this.loadedFroala ?
|
|
||||||
m('div', {
|
|
||||||
oncreate: function(div) {
|
|
||||||
vnode.state.froala = new FroalaEditor(div.dom, EditPage.getFroalaOptions(), function() {
|
|
||||||
vnode.state.froala.html.set(vnode.state.page.description)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
: null
|
|
||||||
),
|
|
||||||
m('label', 'Path'),
|
m('label', 'Path'),
|
||||||
m('input', {
|
m('input', {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: this.page.path,
|
value: this.data.page.path,
|
||||||
oninput: this.updateValue.bind(this, 'path'),
|
oninput: this.updateValue.bind(this, 'path'),
|
||||||
}),
|
}),
|
||||||
m('div.loading-spinner', { hidden: this.loadedFroala }),
|
]),
|
||||||
|
]),
|
||||||
|
m('label', 'Description'),
|
||||||
|
m(Editor, {
|
||||||
|
oncreate: (subnode) => {
|
||||||
|
this.editor = subnode.state.editor
|
||||||
|
},
|
||||||
|
contentdata: this.data.page.content,
|
||||||
|
}),
|
||||||
|
m('div', {
|
||||||
|
hidden: !this.data.page.name || !this.data.page.path
|
||||||
|
}, [
|
||||||
m('input', {
|
m('input', {
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
value: 'Save',
|
value: 'Save',
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
)
|
: m('div.error', {
|
||||||
|
hidden: !this.error,
|
||||||
|
onclick: () => { this.fetchPage(vnode) },
|
||||||
|
}, this.error),,
|
||||||
|
]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,40 +1,32 @@
|
||||||
const Page = require('../api/page')
|
const Page = require('../api/page.p')
|
||||||
const Dialogue = require('../widgets/dialogue')
|
const Dialogue = require('../widgets/dialogue')
|
||||||
|
const common = require('../api/common')
|
||||||
|
|
||||||
const AdminPages = {
|
const AdminPages = {
|
||||||
parseTree: function(pages) {
|
|
||||||
let map = new Map()
|
|
||||||
for (let i = 0; i < pages.length; i++) {
|
|
||||||
pages[i].children = []
|
|
||||||
map.set(pages[i].id, pages[i])
|
|
||||||
}
|
|
||||||
for (let i = 0; i < pages.length; i++) {
|
|
||||||
if (pages[i].parent_id && map.has(pages[i].parent_id)) {
|
|
||||||
map.get(pages[i].parent_id).children.push(pages[i])
|
|
||||||
pages.splice(i, 1)
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pages
|
|
||||||
},
|
|
||||||
|
|
||||||
oninit: function(vnode) {
|
oninit: function(vnode) {
|
||||||
this.loading = true
|
|
||||||
this.error = ''
|
this.error = ''
|
||||||
this.pages = []
|
this.pages = []
|
||||||
this.removePage = null
|
this.removePage = null
|
||||||
|
|
||||||
document.title = 'Pages - Admin NFP Moe'
|
document.title = 'Pages - Admin NFP Moe'
|
||||||
|
this.fetchPages(vnode)
|
||||||
|
},
|
||||||
|
|
||||||
Page.getAllPages()
|
fetchPages: function(vnode) {
|
||||||
.then(function(result) {
|
this.loading = true
|
||||||
vnode.state.pages = AdminPages.parseTree(result)
|
this.error = ''
|
||||||
|
|
||||||
|
return common.sendRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/auth/pages',
|
||||||
})
|
})
|
||||||
.catch(function(err) {
|
.then((result) => {
|
||||||
vnode.state.error = err.message
|
this.pages = result.tree
|
||||||
|
}, (err) => {
|
||||||
|
this.error = err.message
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(() => {
|
||||||
vnode.state.loading = false
|
this.loading = false
|
||||||
m.redraw()
|
m.redraw()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -43,13 +35,21 @@ const AdminPages = {
|
||||||
let removingPage = this.removePage
|
let removingPage = this.removePage
|
||||||
this.removePage = null
|
this.removePage = null
|
||||||
this.loading = true
|
this.loading = true
|
||||||
Page.removePage(removingPage, removingPage.id)
|
|
||||||
.then(this.oninit.bind(this, vnode))
|
|
||||||
.catch(function(err) {
|
|
||||||
vnode.state.error = err.message
|
|
||||||
vnode.state.loading = false
|
|
||||||
m.redraw()
|
m.redraw()
|
||||||
|
|
||||||
|
return common.sendRequest({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/api/auth/pages/' + removingPage.id,
|
||||||
})
|
})
|
||||||
|
.then(() => Page.refreshTree())
|
||||||
|
.then(
|
||||||
|
() => this.fetchPages(vnode),
|
||||||
|
(err) => {
|
||||||
|
this.error = err.message
|
||||||
|
this.loading = false
|
||||||
|
m.redraw()
|
||||||
|
}
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
drawPage: function(vnode, page) {
|
drawPage: function(vnode, page) {
|
||||||
|
@ -63,7 +63,7 @@ const AdminPages = {
|
||||||
m('td.right', page.updated_at.replace('T', ' ').split('.')[0]),
|
m('td.right', page.updated_at.replace('T', ' ').split('.')[0]),
|
||||||
m('td.right', m('button', { onclick: function() { vnode.state.removePage = page } }, 'Remove')),
|
m('td.right', m('button', { onclick: function() { vnode.state.removePage = page } }, 'Remove')),
|
||||||
]),
|
]),
|
||||||
].concat(page.children.map(AdminPages.drawPage.bind(this, vnode)))
|
].concat(page.children ? page.children.map(AdminPages.drawPage.bind(this, vnode)) : [])
|
||||||
},
|
},
|
||||||
|
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
|
@ -79,7 +79,7 @@ const AdminPages = {
|
||||||
m('header', m('h1', 'All pages')),
|
m('header', m('h1', 'All pages')),
|
||||||
m('div.error', {
|
m('div.error', {
|
||||||
hidden: !this.error,
|
hidden: !this.error,
|
||||||
onclick: function() { vnode.state.error = '' },
|
onclick: () => { this.fetchPages(vnode) },
|
||||||
}, this.error),
|
}, this.error),
|
||||||
m('table', [
|
m('table', [
|
||||||
m('thead',
|
m('thead',
|
||||||
|
|
|
@ -25,6 +25,21 @@ exports.createPage = function(body) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processPageBranch(arr, branches, prefix) {
|
||||||
|
branches.forEach((page) => {
|
||||||
|
arr.push({ id: page.id, name: prefix + page.name })
|
||||||
|
if (page.children && page.children.length) {
|
||||||
|
processPageBranch(arr, page.children, page.name + ' -> ')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getFlatTree = function() {
|
||||||
|
let arr = []
|
||||||
|
processPageBranch(arr, Tree, '')
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
exports.getTree = function() {
|
exports.getTree = function() {
|
||||||
return common.sendRequest({
|
return common.sendRequest({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|
|
@ -18,9 +18,37 @@ function parseLeaf(tree) {
|
||||||
|
|
||||||
parseLeaf(Tree)
|
parseLeaf(Tree)
|
||||||
|
|
||||||
|
function processPageBranch(arr, branches, prefix) {
|
||||||
|
branches.forEach((page) => {
|
||||||
|
arr.push({ id: page.id, name: prefix + page.name })
|
||||||
|
if (page.children && page.children.length) {
|
||||||
|
processPageBranch(arr, page.children, page.name + ' -> ')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getFlatTree = function() {
|
||||||
|
let arr = []
|
||||||
|
processPageBranch(arr, Tree, '')
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
exports.getPage = function(path, page) {
|
exports.getPage = function(path, page) {
|
||||||
return common.sendRequest({
|
return common.sendRequest({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/api/' + (path ? 'pages/' + path : 'frontpage') + '?page=' + (page || 1),
|
url: '/api/' + (path ? 'pages/' + path : 'frontpage') + '?page=' + (page || 1),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.refreshTree = function() {
|
||||||
|
return common.sendRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/pagetree',
|
||||||
|
})
|
||||||
|
.then(pages => {
|
||||||
|
Tree.splice(0, Tree.length)
|
||||||
|
Tree.push.apply(Tree, pages.tree)
|
||||||
|
TreeMap.clear()
|
||||||
|
parseLeaf(Tree)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
70
app/app.scss
70
app/app.scss
|
@ -32,6 +32,7 @@ ol, ul {
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spinner-loader {
|
@keyframes spinner-loader {
|
||||||
|
@ -274,3 +275,72 @@ input[type="reset"]::-moz-focus-inner {
|
||||||
a { color: $dark_secondary-dark-bg; }
|
a { color: $dark_secondary-dark-bg; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--primary-bg: #01579b;
|
||||||
|
--primary-fg: white;
|
||||||
|
--primary-fg-url: #FFC7C7;
|
||||||
|
--primary-light-bg: #3D77C7; // #4f83cc;
|
||||||
|
--primary-light-fg: white;
|
||||||
|
--primary-dark-bg: #002f6c;
|
||||||
|
--primary-dark-fg: white;
|
||||||
|
|
||||||
|
--secondary-bg: #f57c00;
|
||||||
|
--secondary-fg: black;
|
||||||
|
--secondary-light-bg: #ffad42;
|
||||||
|
--secondary-light-fg: black;
|
||||||
|
--secondary-dark-bg: #bb4d00;
|
||||||
|
--secondary-dark-fg: white;
|
||||||
|
|
||||||
|
--table-fg: #333;
|
||||||
|
--border: #ccc;
|
||||||
|
--border-fg: black;
|
||||||
|
--border-fg-url: #8f3c00;
|
||||||
|
--title-fg: #555;
|
||||||
|
--meta-fg: #757575; // #999
|
||||||
|
--meta-light-fg: #999999;
|
||||||
|
|
||||||
|
--main-bg: white;
|
||||||
|
--main-fg: black;
|
||||||
|
--input-bg: white;
|
||||||
|
--input-border: #333;
|
||||||
|
--input-fg: black;
|
||||||
|
|
||||||
|
--newsitem-bg: transparent;
|
||||||
|
--newsitem-border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmodeon {
|
||||||
|
--primary-bg: #013b68;
|
||||||
|
--primary-fg: white;
|
||||||
|
--primary-fg-url: #FFC7C7;
|
||||||
|
--primary-light-bg: #28518B;
|
||||||
|
--primary-light-fg: white;
|
||||||
|
--primary-dark-bg: #002f6c;
|
||||||
|
--primary-dark-fg: white;
|
||||||
|
|
||||||
|
--secondary-bg: #e05e00;
|
||||||
|
--secondary-fg: black;
|
||||||
|
--secondary-light-bg: #ffad42;
|
||||||
|
--secondary-light-fg: black;
|
||||||
|
--secondary-dark-bg: #e05e00;
|
||||||
|
--secondary-dark-fg: white;
|
||||||
|
--secondary-darker-fg: #fe791b;
|
||||||
|
|
||||||
|
--table-fg: #333;
|
||||||
|
--border: #343536;
|
||||||
|
--border-fg: #d7dadc;;
|
||||||
|
--border-fg-url: #e05e00;
|
||||||
|
--title-fg: #808080;
|
||||||
|
--meta-fg: hsl(0, 0%, 55%);
|
||||||
|
--meta-light-fg: #999999;
|
||||||
|
|
||||||
|
--main-bg: black;
|
||||||
|
--main-fg: #d7dadc;
|
||||||
|
--input-bg: #272729;
|
||||||
|
--input-border: #343536;
|
||||||
|
--input-fg: white;
|
||||||
|
|
||||||
|
--newsitem-bg: #1a1a1b;
|
||||||
|
--newsitem-border: 1px solid #343536;
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ const m = require('mithril')
|
||||||
const ApiArticle = require('../api/article.p')
|
const ApiArticle = require('../api/article.p')
|
||||||
const Authentication = require('../authentication')
|
const Authentication = require('../authentication')
|
||||||
const Fileinfo = require('../widgets/fileinfo')
|
const Fileinfo = require('../widgets/fileinfo')
|
||||||
|
const EditorBlock = require('../widgets/editorblock')
|
||||||
|
|
||||||
const Article = {
|
const Article = {
|
||||||
oninit: function(vnode) {
|
oninit: function(vnode) {
|
||||||
|
@ -11,6 +12,10 @@ const Article = {
|
||||||
this.data = {
|
this.data = {
|
||||||
article: null,
|
article: null,
|
||||||
files: [],
|
files: [],
|
||||||
|
pictureFallback: null,
|
||||||
|
pictureJpeg: null,
|
||||||
|
pictureAvif: null,
|
||||||
|
pictureCover: null,
|
||||||
}
|
}
|
||||||
this.showcomments = false
|
this.showcomments = false
|
||||||
|
|
||||||
|
@ -52,6 +57,24 @@ const Article = {
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
this.data = result
|
this.data = result
|
||||||
|
|
||||||
|
if (this.data.article.media_alt_prefix) {
|
||||||
|
this.data.article.pictureFallback = this.data.article.media_alt_prefix + '_small.jpg'
|
||||||
|
this.data.article.pictureJpeg = this.data.article.media_alt_prefix + '_small.jpg' + ' 720w, '
|
||||||
|
+ this.data.article.media_alt_prefix + '_medium.jpg' + ' 1300w, '
|
||||||
|
+ this.data.article.media_alt_prefix + '_large.jpg 1920w'
|
||||||
|
this.data.article.pictureAvif = this.data.article.media_alt_prefix + '_small.avif' + ' 720w, '
|
||||||
|
+ this.data.article.media_alt_prefix + '_medium.avif' + ' 1300w, '
|
||||||
|
+ this.data.article.media_alt_prefix + '_large.avif 1920w'
|
||||||
|
|
||||||
|
this.data.article.pictureCover = '(max-width: 840px) calc(100vw - 82px), '
|
||||||
|
+ '758px'
|
||||||
|
} else {
|
||||||
|
this.data.article.pictureFallback = null
|
||||||
|
this.data.article.pictureJpeg = null
|
||||||
|
this.data.article.pictureAvif = null
|
||||||
|
this.data.article.pictureCover = null
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.data.article) {
|
if (!this.data.article) {
|
||||||
this.error = 'Article not found'
|
this.error = 'Article not found'
|
||||||
}
|
}
|
||||||
|
@ -67,19 +90,7 @@ const Article = {
|
||||||
},
|
},
|
||||||
|
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
var deviceWidth = window.innerWidth
|
let article = this.data.article
|
||||||
var imagePath = ''
|
|
||||||
|
|
||||||
if (this.data.article && this.data.article.media) {
|
|
||||||
var pixelRatio = window.devicePixelRatio || 1
|
|
||||||
if ((deviceWidth < 800 && pixelRatio <= 1)
|
|
||||||
|| (deviceWidth < 600 && pixelRatio > 1)) {
|
|
||||||
imagePath = this.data.article.media.medium_url
|
|
||||||
} else {
|
|
||||||
imagePath = this.data.article.media.large_url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this.loading ?
|
this.loading ?
|
||||||
m('article.article', m('div.loading-spinner'))
|
m('article.article', m('div.loading-spinner'))
|
||||||
|
@ -91,37 +102,53 @@ const Article = {
|
||||||
},
|
},
|
||||||
}, 'Article error: ' + this.error))
|
}, 'Article error: ' + this.error))
|
||||||
: m('article.article', [
|
: m('article.article', [
|
||||||
this.data.article.page_path
|
article.page_path
|
||||||
? m('div.goback', ['« ', m(m.route.Link, { href: '/page/' + this.data.article.page_path }, this.data.article.page_name)])
|
? m('div.goback', ['« ', m(m.route.Link, { href: '/page/' + article.page_path }, article.page_name)])
|
||||||
: null,
|
: null,
|
||||||
m('header', m('h1', this.data.article.name)),
|
m('header', m('h1', article.name)),
|
||||||
m('.fr-view', [
|
m('.fr-view', [
|
||||||
this.data.article.media
|
article.pictureFallback
|
||||||
? m('a.cover', {
|
? m('a.cover', {
|
||||||
rel: 'noopener',
|
rel: 'noopener',
|
||||||
href: this.data.article.media.link,
|
href: article.media_path,
|
||||||
}, m('img', { src: imagePath, alt: 'Cover image for ' + this.data.article.name }))
|
}, [
|
||||||
|
m('picture', [
|
||||||
|
m('source', {
|
||||||
|
srcset: article.pictureAvif,
|
||||||
|
sizes: article.pictureCover,
|
||||||
|
type: 'image/avif',
|
||||||
|
}),
|
||||||
|
m('img', {
|
||||||
|
srcset: article.pictureJpeg,
|
||||||
|
sizes: article.pictureCover,
|
||||||
|
alt: 'Image for news item ' + article.name,
|
||||||
|
src: article.pictureFallback,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
])
|
||||||
: null,
|
: null,
|
||||||
this.data.article.content ? m.trust(this.data.article.content) : null,
|
article.content.blocks.map(block => {
|
||||||
|
return m(EditorBlock, { block: block })
|
||||||
|
}),
|
||||||
this.data.files.map(function(file) {
|
this.data.files.map(function(file) {
|
||||||
return m(Fileinfo, { file: file })
|
return m(Fileinfo, { file: file })
|
||||||
}),
|
}),
|
||||||
m('div.entrymeta', [
|
m('div.entrymeta', [
|
||||||
'Posted ',
|
'Posted ',
|
||||||
this.data.article.page_path
|
article.page_path
|
||||||
? [
|
? [
|
||||||
'in',
|
'in',
|
||||||
m(m.route.Link, { href: '/page/' + this.data.article.page_path }, this.data.article.page_name)
|
m(m.route.Link, { href: '/page/' + article.page_path }, article.page_name)
|
||||||
]
|
]
|
||||||
: '',
|
: '',
|
||||||
'at ' + (this.data.article.publish_at.replace('T', ' ').split('.')[0]).substr(0, 16),
|
'at ' + (article.publish_at.replace('T', ' ').split('.')[0]).substr(0, 16),
|
||||||
' by ' + (this.data.article.admin_name || 'Admin'),
|
' by ' + (article.admin_name || 'Admin'),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
Authentication.currentUser
|
Authentication.currentUser
|
||||||
? m('div.admin-actions', [
|
? m('div.admin-actions', [
|
||||||
m('span', 'Admin controls:'),
|
m('span', 'Admin controls:'),
|
||||||
m(m.route.Link, { href: '/admin/articles/' + this.data.article.path }, 'Edit article'),
|
m(m.route.Link, { href: '/admin/articles/' + article.path }, 'Edit article'),
|
||||||
])
|
])
|
||||||
: null,
|
: null,
|
||||||
this.showcomments
|
this.showcomments
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const storageName = 'logintoken'
|
const storageName = 'nfp_sites_logintoken'
|
||||||
|
|
||||||
const Authentication = {
|
const Authentication = {
|
||||||
currentUser: null,
|
currentUser: null,
|
||||||
|
@ -37,4 +37,6 @@ const Authentication = {
|
||||||
|
|
||||||
Authentication.updateToken(localStorage.getItem(storageName))
|
Authentication.updateToken(localStorage.getItem(storageName))
|
||||||
|
|
||||||
|
window.Authentication = Authentication
|
||||||
|
|
||||||
module.exports = Authentication
|
module.exports = Authentication
|
||||||
|
|
|
@ -16,7 +16,7 @@ const Footer = {
|
||||||
Page.Tree.map(function(page) {
|
Page.Tree.map(function(page) {
|
||||||
return [
|
return [
|
||||||
m(m.route.Link, { class: 'root', href: '/page/' + page.path }, page.name),
|
m(m.route.Link, { class: 'root', href: '/page/' + page.path }, page.name),
|
||||||
(page.children.length
|
(page.children
|
||||||
? m('ul', page.children.map(function(subpage) {
|
? m('ul', page.children.map(function(subpage) {
|
||||||
return m('li', m(m.route.Link, { class: 'child', href: '/page/' + subpage.path }, subpage.name))
|
return m('li', m(m.route.Link, { class: 'child', href: '/page/' + subpage.path }, subpage.name))
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -74,7 +74,7 @@ const Frontpage = {
|
||||||
|
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
var deviceWidth = window.innerWidth
|
var deviceWidth = window.innerWidth
|
||||||
var bannerPath = this.data.featured && this.data.featured.banner_prefix
|
var bannerPath = this.data.featured && this.data.featured.banner_alt_prefix
|
||||||
|
|
||||||
if (bannerPath) {
|
if (bannerPath) {
|
||||||
var pixelRatio = window.devicePixelRatio || 1
|
var pixelRatio = window.devicePixelRatio || 1
|
||||||
|
|
|
@ -9,12 +9,6 @@ m.route.set = function(path, data, options){
|
||||||
window.scrollTo(0, 0)
|
window.scrollTo(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*console.log('tree', window.__nfptree)
|
|
||||||
console.log('featured', window.__nfpfeatured)
|
|
||||||
console.log('data', window.__nfpdata)
|
|
||||||
console.log('subdata', window.__nfpsubdata)
|
|
||||||
console.log('links', window.__nfplinks)*/
|
|
||||||
|
|
||||||
m.route.linkOrig = m.route.link
|
m.route.linkOrig = m.route.link
|
||||||
m.route.link = function(vnode){
|
m.route.link = function(vnode){
|
||||||
m.route.linkOrig(vnode)
|
m.route.linkOrig(vnode)
|
||||||
|
@ -40,7 +34,6 @@ const onLoaded = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const onError = function(a, b, c) {
|
const onError = function(a, b, c) {
|
||||||
console.log('onError', this, a, b, c)
|
|
||||||
elements.forEach(function(x) { x.remove() })
|
elements.forEach(function(x) { x.remove() })
|
||||||
loadedAdmin = loadingAdmin = false
|
loadedAdmin = loadingAdmin = false
|
||||||
loaded = 0
|
loaded = 0
|
||||||
|
|
|
@ -45,7 +45,6 @@ const Login = {
|
||||||
if (!result.token) {
|
if (!result.token) {
|
||||||
return Promise.reject(new Error('Server authentication down.'))
|
return Promise.reject(new Error('Server authentication down.'))
|
||||||
}
|
}
|
||||||
console.log(result)
|
|
||||||
Authentication.updateToken(result.token)
|
Authentication.updateToken(result.token)
|
||||||
m.route.set(Login.redirect || '/')
|
m.route.set(Login.redirect || '/')
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,11 +22,7 @@ const Menu = {
|
||||||
|
|
||||||
Menu.loading = true
|
Menu.loading = true
|
||||||
|
|
||||||
Page.getTree()
|
Page.refreshTree()
|
||||||
.then(function(results) {
|
|
||||||
Page.Tree.splice(0, Page.Tree.length)
|
|
||||||
Page.Tree.push.apply(Page.Tree, results)
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
Menu.error = err.message
|
Menu.error = err.message
|
||||||
})
|
})
|
||||||
|
@ -37,6 +33,7 @@ const Menu = {
|
||||||
},
|
},
|
||||||
|
|
||||||
view: function() {
|
view: function() {
|
||||||
|
console.log('menu view', Boolean(Authentication.currentUser))
|
||||||
return [
|
return [
|
||||||
m('div.top', [
|
m('div.top', [
|
||||||
m(m.route.Link,
|
m(m.route.Link,
|
||||||
|
@ -73,7 +70,7 @@ const Menu = {
|
||||||
class: Menu.currentActive === 'home' ? 'active' : '',
|
class: Menu.currentActive === 'home' ? 'active' : '',
|
||||||
}, 'Home'),
|
}, 'Home'),
|
||||||
Menu.loading ? m('div.loading-spinner') : Page.Tree.map(function(page) {
|
Menu.loading ? m('div.loading-spinner') : Page.Tree.map(function(page) {
|
||||||
if (page.children.length) {
|
if (page.children) {
|
||||||
return m('div.hassubmenu', [
|
return m('div.hassubmenu', [
|
||||||
m(m.route.Link, {
|
m(m.route.Link, {
|
||||||
href: '/page/' + page.path,
|
href: '/page/' + page.path,
|
||||||
|
|
|
@ -156,7 +156,7 @@ const Page = {
|
||||||
? m('aside.news', [
|
? m('aside.news', [
|
||||||
m('h4', 'Latest posts under ' + this.data.page.name + ':'),
|
m('h4', 'Latest posts under ' + this.data.page.name + ':'),
|
||||||
this.data.articles.map(function(article) {
|
this.data.articles.map(function(article) {
|
||||||
return m(Newsentry, article)
|
return m(Newsentry, { article: article })
|
||||||
}),
|
}),
|
||||||
m(Pages, {
|
m(Pages, {
|
||||||
base: '/page/' + this.data.page.path,
|
base: '/page/' + this.data.page.path,
|
||||||
|
@ -171,7 +171,7 @@ const Page = {
|
||||||
imagePath ? m('a', { href: this.data.page.media.link}, m('img.page-cover', { src: imagePath, alt: 'Cover image for ' + this.data.page.name } )) : null,
|
imagePath ? m('a', { href: this.data.page.media.link}, m('img.page-cover', { src: imagePath, alt: 'Cover image for ' + this.data.page.name } )) : null,
|
||||||
m('h4', 'Latest posts under ' + this.data.page.name + ':'),
|
m('h4', 'Latest posts under ' + this.data.page.name + ':'),
|
||||||
this.data.articles.map(function(article) {
|
this.data.articles.map(function(article) {
|
||||||
return m(Newsentry, article)
|
return m(Newsentry, { article: article })
|
||||||
}),
|
}),
|
||||||
m(Pages, {
|
m(Pages, {
|
||||||
base: '/page/' + this.data.page.path,
|
base: '/page/' + this.data.page.path,
|
||||||
|
|
61
app/widgets/editorblock.js
Normal file
61
app/widgets/editorblock.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
Blocks:
|
||||||
|
* Paragraph
|
||||||
|
* Header
|
||||||
|
* SimpleImage
|
||||||
|
* Quote
|
||||||
|
* CodeTool
|
||||||
|
* List
|
||||||
|
* Delimiter
|
||||||
|
* RawTool
|
||||||
|
|
||||||
|
Other:
|
||||||
|
* InlineCode
|
||||||
|
*/
|
||||||
|
|
||||||
|
const EditorBlock = {
|
||||||
|
oninit: function(vnode) {
|
||||||
|
this.id = null
|
||||||
|
this.output = null
|
||||||
|
this.onbeforeupdate(vnode)
|
||||||
|
},
|
||||||
|
|
||||||
|
onbeforeupdate: function(vnode) {
|
||||||
|
if (!vnode.attrs.block && !this.id) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (vnode.attrs.block && vnode.attrs.block.id
|
||||||
|
&& vnode.attrs.block.id === this.id) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vnode.attrs.block && vnode.attrs.block.id
|
||||||
|
&& vnode.attrs.block.id !== this.id) {
|
||||||
|
this.renderblock(vnode)
|
||||||
|
} else {
|
||||||
|
this.output = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
renderblock: function(vnode) {
|
||||||
|
let block = vnode.attrs.block
|
||||||
|
this.id = block.id
|
||||||
|
switch (block.type) {
|
||||||
|
case 'paragraph':
|
||||||
|
this.output = m('p', m.trust(block.data.text))
|
||||||
|
break
|
||||||
|
case 'htmlraw':
|
||||||
|
this.output = m.trust(block.data.html)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this.output = m('p', m.trust(block))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
view: function(vnode) {
|
||||||
|
return this.output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = EditorBlock
|
|
@ -1,6 +1,11 @@
|
||||||
const Fileinfo = require('./fileinfo')
|
const Fileinfo = require('./fileinfo')
|
||||||
|
|
||||||
const Newsentry = {
|
const Newsentry = {
|
||||||
|
oninit: function(vnode) {
|
||||||
|
this.lastId = null
|
||||||
|
this.onbeforeupdate(vnode)
|
||||||
|
},
|
||||||
|
|
||||||
strip: function(html) {
|
strip: function(html) {
|
||||||
var doc = new DOMParser().parseFromString(html, 'text/html')
|
var doc = new DOMParser().parseFromString(html, 'text/html')
|
||||||
var out = doc.body.textContent || ''
|
var out = doc.body.textContent || ''
|
||||||
|
@ -11,43 +16,80 @@ const Newsentry = {
|
||||||
return out
|
return out
|
||||||
},
|
},
|
||||||
|
|
||||||
view: function(vnode) {
|
onbeforeupdate: function(vnode) {
|
||||||
var deviceWidth = window.innerWidth
|
let article = vnode.attrs.article
|
||||||
var pixelRatio = window.devicePixelRatio || 1
|
|
||||||
var imagePath = ''
|
|
||||||
|
|
||||||
if (vnode.attrs.media) {
|
if (this.lastId !== article.id) {
|
||||||
if (deviceWidth > 440 || pixelRatio <= 1) {
|
this.lastId = article.id
|
||||||
imagePath = vnode.attrs.media.small_url
|
this.description = null
|
||||||
|
|
||||||
|
for (let i = 0; i < article.content.blocks.length; i++) {
|
||||||
|
if (article.content.blocks[i].type === 'paragraph') {
|
||||||
|
this.description = article.content.blocks[i].data.text
|
||||||
|
break
|
||||||
|
} else if (article.content.blocks[i].type === 'htmlraw') {
|
||||||
|
this.description = this.strip(article.content.blocks[i].data.html)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (article.media_alt_prefix) {
|
||||||
|
this.pictureFallback = article.media_alt_prefix + '_small.jpg'
|
||||||
|
this.pictureJpeg = article.media_alt_prefix + '_small.jpg' + ' 720w, '
|
||||||
|
+ article.media_alt_prefix + '_medium.jpg' + ' 1300w, '
|
||||||
|
+ article.media_alt_prefix + '_large.jpg 1920w'
|
||||||
|
this.pictureAvif = article.media_alt_prefix + '_small.avif' + ' 720w, '
|
||||||
|
+ article.media_alt_prefix + '_medium.avif' + ' 1300w, '
|
||||||
|
+ article.media_alt_prefix + '_large.avif 1920w'
|
||||||
|
|
||||||
|
this.pictureCover = '(max-width: 440px) calc(100vw - 40px), '
|
||||||
|
+ '124px'
|
||||||
} else {
|
} else {
|
||||||
imagePath = vnode.attrs.media.medium_url
|
this.pictureFallback = null
|
||||||
|
this.pictureJpeg = null
|
||||||
|
this.pictureAvif = null
|
||||||
|
this.pictureCover = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
view: function(vnode) {
|
||||||
|
let article = vnode.attrs.article
|
||||||
|
|
||||||
return m('newsentry', [
|
return m('newsentry', [
|
||||||
imagePath
|
this.pictureFallback
|
||||||
? m(m.route.Link, {
|
? m(m.route.Link, {
|
||||||
class: 'cover',
|
class: 'cover',
|
||||||
href: '/article/' + vnode.attrs.path,
|
href: '/article/' + article.path,
|
||||||
}, m('picture', [
|
},
|
||||||
m('source', { srcset:
|
m('picture', [
|
||||||
vnode.attrs.media.small_url + ''
|
m('source', {
|
||||||
|
srcset: this.pictureAvif,
|
||||||
|
sizes: this.pictureCover,
|
||||||
|
type: 'image/avif',
|
||||||
}),
|
}),
|
||||||
m('img', { src: imagePath, alt: 'Article image for ' + vnode.attrs.name }),
|
m('img', {
|
||||||
]))
|
srcset: this.pictureJpeg,
|
||||||
|
sizes: this.pictureCover,
|
||||||
|
alt: 'Image for news item ' + article.name,
|
||||||
|
src: this.pictureFallback,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
: m('a.cover.nobg'),
|
: m('a.cover.nobg'),
|
||||||
m('div.entrycontent', [
|
m('div.entrycontent', [
|
||||||
m('div.title', [
|
m('div.title', [
|
||||||
m(m.route.Link,
|
m(m.route.Link,
|
||||||
{ href: '/article/' + vnode.attrs.path },
|
{ href: '/article/' + article.path },
|
||||||
m('h3', [vnode.attrs.name])
|
m('h3', [article.name])
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
(vnode.attrs.files && vnode.attrs.files.length
|
(article.files && article.files.length
|
||||||
? vnode.attrs.files.map(function(file) {
|
? article.files.map(function(file) {
|
||||||
return m(Fileinfo, { file: file, slim: true })
|
return m(Fileinfo, { file: file, slim: true })
|
||||||
})
|
})
|
||||||
: vnode.attrs.description
|
: this.description
|
||||||
? m('span.entrydescription', Newsentry.strip(vnode.attrs.description))
|
? m('span.entrydescription', this.description)
|
||||||
: null),
|
: null),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
|
|
@ -1,25 +1,36 @@
|
||||||
const Fileinfo = require('./fileinfo')
|
const Fileinfo = require('./fileinfo')
|
||||||
|
const EditorBlock = require('./editorblock')
|
||||||
|
|
||||||
const Newsitem = {
|
const Newsitem = {
|
||||||
oninit: function(vnode) {
|
oninit: function(vnode) {
|
||||||
let article = vnode.attrs.article
|
this.lastId = null
|
||||||
if (article.media_prefix) {
|
this.onbeforeupdate(vnode)
|
||||||
this.fallbackImage = article.media_prefix + '_small.jpg'
|
},
|
||||||
this.srcsetJpeg = article.media_prefix + '_small.jpg' + ' 720w, '
|
|
||||||
+ article.media_prefix + '_medium.jpg' + ' 1300w, '
|
|
||||||
+ article.media_prefix + '_large.jpg'
|
|
||||||
this.srcsetAvif = article.media_prefix + '_small.avif' + ' 720w, '
|
|
||||||
+ article.media_prefix + '_medium.avif' + ' 1300w, '
|
|
||||||
+ article.media_prefix + '_large.avif'
|
|
||||||
|
|
||||||
this.coverSizes = '(max-width: 639px) calc(100vw - 40px), '
|
onbeforeupdate: function(vnode) {
|
||||||
|
let article = vnode.attrs.article
|
||||||
|
|
||||||
|
if (this.lastId !== article.id) {
|
||||||
|
this.lastId = article.id
|
||||||
|
|
||||||
|
if (article.media_alt_prefix) {
|
||||||
|
this.pictureFallback = article.media_alt_prefix + '_small.jpg'
|
||||||
|
this.pictureJpeg = article.media_alt_prefix + '_small.jpg' + ' 720w, '
|
||||||
|
+ article.media_alt_prefix + '_medium.jpg' + ' 1300w, '
|
||||||
|
+ article.media_alt_prefix + '_large.jpg 1920w'
|
||||||
|
this.pictureAvif = article.media_alt_prefix + '_small.avif' + ' 720w, '
|
||||||
|
+ article.media_alt_prefix + '_medium.avif' + ' 1300w, '
|
||||||
|
+ article.media_alt_prefix + '_large.avif 1920w'
|
||||||
|
|
||||||
|
this.pictureCover = '(max-width: 639px) calc(100vw - 40px), '
|
||||||
+ '(max-width: 1000px) 300px, '
|
+ '(max-width: 1000px) 300px, '
|
||||||
+ '400px'
|
+ '400px'
|
||||||
} else {
|
} else {
|
||||||
this.fallbackImage = null
|
this.pictureFallback = null
|
||||||
this.srcsetJpeg = null
|
this.pictureJpeg = null
|
||||||
this.srcsetAvif = null
|
this.pictureAvif = null
|
||||||
this.coverSizes = null
|
this.pictureCover = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -32,32 +43,30 @@ const Newsitem = {
|
||||||
m('h3', [article.name])
|
m('h3', [article.name])
|
||||||
),
|
),
|
||||||
m('div.newsitemcontent', [
|
m('div.newsitemcontent', [
|
||||||
this.fallbackImage
|
this.pictureFallback
|
||||||
? m(m.route.Link, {
|
? m(m.route.Link, {
|
||||||
class: 'cover',
|
class: 'cover',
|
||||||
href: '/article/' + article.path,
|
href: '/article/' + article.path,
|
||||||
},
|
},
|
||||||
m('picture', [
|
m('picture', [
|
||||||
this.srcsetAvif ? m('source', {
|
m('source', {
|
||||||
srcset: this.srcsetAvif,
|
srcset: this.pictureAvif,
|
||||||
sizes: this.coverSizes,
|
sizes: this.pictureCover,
|
||||||
type: 'image/avif',
|
type: 'image/avif',
|
||||||
}) : null,
|
}),
|
||||||
m('img', {
|
m('img', {
|
||||||
srcset: this.srcsetJpeg,
|
srcset: this.pictureJpeg,
|
||||||
sizes: this.coverSizes,
|
sizes: this.pictureCover,
|
||||||
alt: 'Image for news item ' + article.name,
|
alt: 'Image for news item ' + article.name,
|
||||||
src: this.fallbackImage,
|
src: this.pictureFallback,
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
m('div.entrycontent', {
|
m('div.entrycontent', [
|
||||||
class: article.media ? '' : 'extrapadding',
|
article.content.blocks.map(block => {
|
||||||
}, [
|
return m(EditorBlock, { block: block })
|
||||||
(article.content
|
}),
|
||||||
? m('.fr-view', m.trust(article.content))
|
|
||||||
: null),
|
|
||||||
(article.files && article.files.length
|
(article.files && article.files.length
|
||||||
? article.files.map(function(file) {
|
? article.files.map(function(file) {
|
||||||
return m(Fileinfo, { file: file, trim: true })
|
return m(Fileinfo, { file: file, trim: true })
|
||||||
|
|
Loading…
Reference in a new issue