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 { parseArticles, parseArticle } from './util.mjs'
|
||||
import { upload } from '../media/upload.mjs'
|
||||
import { mediaToDatabase } from '../media/util.mjs'
|
||||
|
||||
export default class ArticleRoutes {
|
||||
constructor(opts = {}) {
|
||||
|
@ -11,7 +12,7 @@ export default class ArticleRoutes {
|
|||
|
||||
/** GET: /api/articles/[path] */
|
||||
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 = {
|
||||
article: parseArticle(res.results[0][0]),
|
||||
|
@ -23,7 +24,7 @@ export default class ArticleRoutes {
|
|||
|
||||
/** GET: /api/auth/articles */
|
||||
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,
|
||||
Math.max(ctx.query.get('page') || 1, 1),
|
||||
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) {
|
||||
let params = [
|
||||
ctx.state.auth_token,
|
||||
ctx.params.path
|
||||
ctx.params.id === '0' ? null : ctx.params.id
|
||||
]
|
||||
if (body) {
|
||||
params = params.concat([
|
||||
|
@ -53,100 +54,28 @@ export default class ArticleRoutes {
|
|||
body.is_featured === 'true' ? 1 : 0,
|
||||
0,
|
||||
])
|
||||
if (banner) {
|
||||
params = params.concat([
|
||||
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,
|
||||
])
|
||||
}
|
||||
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('common.article_auth_get_update_create', params)
|
||||
let res = await ctx.db.safeCallProc('article_auth_get_update_create', params)
|
||||
|
||||
let out = {
|
||||
article: parseArticle(res.results[0][0]),
|
||||
article: parseArticle(res.results[0][0]) || { publish_at: new Date() },
|
||||
files: parseFiles(res.results[1]),
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
/** GET: /api/auth/articles/:path */
|
||||
/** GET: /api/auth/articles/:id */
|
||||
auth_getSingleArticle(ctx) {
|
||||
return this.private_getUpdateArticle(ctx)
|
||||
}
|
||||
|
||||
/** PUT: /api/auth/articles/:path */
|
||||
/** PUT: /api/auth/articles/:id */
|
||||
async auth_updateCreateSingleArticle(ctx) {
|
||||
console.log(ctx.req.body)
|
||||
|
||||
|
@ -173,11 +102,11 @@ export default class ArticleRoutes {
|
|||
return this.private_getUpdateArticle(ctx, ctx.req.body, newBanner, newMedia)
|
||||
}
|
||||
|
||||
/** DELETE: /api/auth/articles/:path */
|
||||
/** DELETE: /api/auth/articles/:id */
|
||||
async auth_removeSingleArticle(ctx) {
|
||||
let params = [
|
||||
ctx.state.auth_token,
|
||||
ctx.params.path,
|
||||
ctx.params.id,
|
||||
// Article data
|
||||
null,
|
||||
null,
|
||||
|
@ -187,25 +116,9 @@ export default class ArticleRoutes {
|
|||
null,
|
||||
null,
|
||||
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)
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { parseFile } from '../file/util.mjs'
|
||||
|
||||
export function parseArticles(articles) {
|
||||
for (let i = 0; i < articles.length; i++) {
|
||||
parseArticle(articles[i])
|
||||
|
@ -5,17 +7,52 @@ export function parseArticles(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) {
|
||||
if (!article) {
|
||||
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) {
|
||||
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) {
|
||||
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
|
||||
}
|
|
@ -13,7 +13,7 @@ export default class AuthenticationRoutes {
|
|||
|
||||
/** GET: /api/authentication/login */
|
||||
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.password,
|
||||
])
|
||||
|
|
|
@ -43,7 +43,7 @@ export function initPool(core, config) {
|
|||
return {
|
||||
safeCallProc: function(name, params, options) {
|
||||
if (name.indexOf('.') < 0) {
|
||||
name = config.schema + '.' + name
|
||||
name = 'common.' + name
|
||||
}
|
||||
return pool.promises.callProc(name, params, options)
|
||||
.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 * as security from './security.mjs'
|
||||
import { parsePagesToTree } from './util.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 {
|
||||
constructor(opts = {}) {
|
||||
Object.assign(this, {
|
||||
Page: opts.Page || Page,
|
||||
security: opts.security || security,
|
||||
upload: upload,
|
||||
})
|
||||
}
|
||||
|
||||
/** GET: /api/pagetree */
|
||||
async getPageTree(ctx) {
|
||||
ctx.body = await this.Page.getTree(ctx)
|
||||
async getPageTree(ctx, onlyReturn = false) {
|
||||
let res = await ctx.db.safeCallProc('pages_get_tree', [])
|
||||
|
||||
if (onlyReturn) {
|
||||
return parsePagesToTree(res.first)
|
||||
}
|
||||
ctx.body = parsePagesToTree(res.first)
|
||||
}
|
||||
|
||||
/** GET: /api/pages/[path] */
|
||||
async getPage(ctx) {
|
||||
ctx.body = await this.Page.getPage(
|
||||
ctx,
|
||||
let res = await ctx.db.safeCallProc('pages_get_single', [
|
||||
ctx.params.path || null,
|
||||
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 fsSync from 'fs'
|
||||
|
||||
import { getTree } from './page/model.mjs'
|
||||
|
||||
export default class ServeHandler {
|
||||
constructor(opts = {}) {
|
||||
Object.assign(this, {
|
||||
pageRoutes: opts.pageRoutes,
|
||||
fs: opts.fs || fs,
|
||||
fsSync: opts.fsSync || fsSync,
|
||||
root: opts.root,
|
||||
|
@ -66,7 +65,7 @@ export default class ServeHandler {
|
|||
}
|
||||
|
||||
try {
|
||||
payload.payloadTree = JSON.stringify(await getTree(ctx))
|
||||
payload.payloadTree = JSON.stringify(await this.pageRoutes.getPageTree(ctx, true))
|
||||
} catch (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/frontpage', 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()
|
||||
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/:path', authenticate(), article.auth_getSingleArticle.bind(article))
|
||||
flaska.put('/api/auth/articles/:path', [authenticate(), FormidableHandler(formidable) ], article.auth_updateCreateSingleArticle.bind(article))
|
||||
// flaska.get('/api/pages/:pageId/articles/public', article.getPublicAllPageArticles.bind(article))
|
||||
flaska.get('/api/auth/articles/:id', authenticate(), article.auth_getSingleArticle.bind(article))
|
||||
flaska.put('/api/auth/articles/:id', [
|
||||
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()
|
||||
flaska.post('/api/authentication/login', JsonHandler(), authentication.login.bind(authentication))
|
||||
|
||||
const serve = new ServeHandler({
|
||||
pageRoutes: page,
|
||||
root: localUtil.getPathFromRoot('../public'),
|
||||
version: core.app.running,
|
||||
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-fg: white;
|
||||
|
|
|
@ -52,7 +52,6 @@ const AdminArticles = {
|
|||
url: '/api/auth/articles?page=' + (this.lastpage || 1),
|
||||
})
|
||||
.then((result) => {
|
||||
console.log(result)
|
||||
this.data = result
|
||||
|
||||
this.data.articles.forEach((article) => {
|
||||
|
@ -75,13 +74,20 @@ const AdminArticles = {
|
|||
let removingArticle = this.removeArticle
|
||||
this.removeArticle = null
|
||||
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()
|
||||
|
||||
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) {
|
||||
|
@ -93,7 +99,7 @@ const AdminArticles = {
|
|||
? '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/' + article.path }, '/article/' + article.path)),
|
||||
m('td.right', article.publish_at.replace('T', ' ').split('.')[0]),
|
||||
|
|
|
@ -16,7 +16,7 @@ const EditArticle = {
|
|||
staff: [],
|
||||
}
|
||||
this.pages = [{id: null, name: 'Frontpage'}]
|
||||
this.addPageTree('', Page.Tree)
|
||||
this.pages = this.pages.concat(Page.getFlatTree())
|
||||
this.newBanner = null
|
||||
this.newMedia = null
|
||||
this.dateInstance = null
|
||||
|
@ -25,15 +25,6 @@ const EditArticle = {
|
|||
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) {
|
||||
if (this.lastid !== m.route.param('id')) {
|
||||
this.fetchArticle(vnode)
|
||||
|
@ -42,16 +33,11 @@ const EditArticle = {
|
|||
|
||||
fetchArticle: function(vnode) {
|
||||
this.lastid = m.route.param('id')
|
||||
let id = this.lastid
|
||||
if (id === 'add') {
|
||||
id = '0'
|
||||
}
|
||||
this.error = ''
|
||||
|
||||
return this.requestArticle(
|
||||
common.sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/auth/articles/' + id,
|
||||
url: '/api/auth/articles/' + (this.lastid === 'add' ? '0' : this.lastid),
|
||||
}))
|
||||
},
|
||||
|
||||
|
@ -75,9 +61,11 @@ const EditArticle = {
|
|||
data
|
||||
.then((result) => {
|
||||
this.data = result
|
||||
if (this.data.article) {
|
||||
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'
|
||||
this.editedPath = true
|
||||
} else {
|
||||
document.title = 'Create Article - Admin NFP Moe'
|
||||
}
|
||||
|
@ -122,16 +110,20 @@ const EditArticle = {
|
|||
},
|
||||
|
||||
mediaRemoved: function(type) {
|
||||
this.data.article[type] = null
|
||||
this.data.article['remove_' + type] = true
|
||||
this.data.article[type + '_prefix'] = null
|
||||
},
|
||||
|
||||
save: function(vnode, e) {
|
||||
e.preventDefault()
|
||||
|
||||
let id = this.lastid
|
||||
if (id === 'add') {
|
||||
id = '0'
|
||||
if (!this.data.article.name) {
|
||||
this.error = 'Name is missing'
|
||||
} else if (!this.data.article.path) {
|
||||
this.error = 'Path is missing'
|
||||
} else {
|
||||
this.error = ''
|
||||
}
|
||||
if (this.error) return
|
||||
|
||||
let formData = new FormData()
|
||||
if (this.newBanner) {
|
||||
|
@ -150,6 +142,8 @@ const EditArticle = {
|
|||
formData.append('path', this.data.article.path)
|
||||
formData.append('page_id', this.data.article.page_id || null)
|
||||
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
|
||||
|
||||
|
@ -160,10 +154,19 @@ const EditArticle = {
|
|||
|
||||
return common.sendRequest({
|
||||
method: 'PUT',
|
||||
url: '/api/auth/articles/' + id,
|
||||
url: '/api/auth/articles/' + (this.lastid === 'add' ? '0' : this.lastid),
|
||||
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),
|
||||
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('div.error', {
|
||||
hidden: !this.error,
|
||||
|
@ -279,7 +286,7 @@ const EditArticle = {
|
|||
this.data.staff.map((item) => {
|
||||
return m('option', {
|
||||
value: item.id,
|
||||
selected: item.id === this.data.article.staff_id
|
||||
selected: item.id === this.data.article.admin_id
|
||||
}, item.name)
|
||||
})
|
||||
),
|
||||
|
@ -293,7 +300,9 @@ const EditArticle = {
|
|||
}),
|
||||
]),
|
||||
]),
|
||||
m('div', [
|
||||
m('div', {
|
||||
hidden: !this.data.article.name || !this.data.article.path
|
||||
}, [
|
||||
m('input', {
|
||||
type: 'submit',
|
||||
value: 'Save',
|
||||
|
|
|
@ -1,245 +1,261 @@
|
|||
const Authentication = require('../authentication')
|
||||
const FileUpload = require('../widgets/fileupload')
|
||||
const Froala = require('./froala')
|
||||
const Page = require('../api/page')
|
||||
const Page = require('../api/page.p')
|
||||
|
||||
const common = require('../api/common')
|
||||
const Editor = require('./editor')
|
||||
|
||||
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) {
|
||||
this.froala = null
|
||||
this.loadedFroala = Froala.loadedFroala
|
||||
|
||||
if (!this.loadedFroala) {
|
||||
Froala.createFroalaScript()
|
||||
.then(function() {
|
||||
vnode.state.loadedFroala = true
|
||||
m.redraw()
|
||||
})
|
||||
this.loading = false
|
||||
this.showLoading = null
|
||||
this.data = {
|
||||
page: null,
|
||||
}
|
||||
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)
|
||||
},
|
||||
|
||||
onupdate: function(vnode) {
|
||||
onbeforeupdate: function(vnode) {
|
||||
if (this.lastid !== m.route.param('id')) {
|
||||
this.fetchPage(vnode)
|
||||
if (this.lastid === 'add') {
|
||||
m.redraw()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
fetchPage: function(vnode) {
|
||||
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') {
|
||||
Page.getPage(this.lastid)
|
||||
.then(function(result) {
|
||||
vnode.state.editedPath = true
|
||||
vnode.state.page = result
|
||||
document.title = 'Editing: ' + result.name + ' - Admin NFP Moe'
|
||||
})
|
||||
.catch(function(err) {
|
||||
vnode.state.error = err.message
|
||||
})
|
||||
.then(function() {
|
||||
vnode.state.loading = false
|
||||
return this.requestPage(
|
||||
common.sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/auth/pages/' + (this.lastid === 'add' ? '0' : this.lastid),
|
||||
}))
|
||||
},
|
||||
|
||||
requestPage: function(data) {
|
||||
this.error = ''
|
||||
|
||||
if (this.showLoading) {
|
||||
clearTimeout(this.showLoading)
|
||||
}
|
||||
|
||||
if (this.data.page) {
|
||||
this.showLoading = setTimeout(() => {
|
||||
this.showLoading = null
|
||||
this.loading = true
|
||||
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 {
|
||||
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) {
|
||||
this.page[name] = e.currentTarget.value
|
||||
this.data.page[name] = e.currentTarget.value
|
||||
if (name === 'path') {
|
||||
this.editedPath = true
|
||||
} 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) {
|
||||
this.page.parent_id = Number(e.currentTarget.value)
|
||||
if (this.page.parent_id === -1) {
|
||||
this.page.parent_id = null
|
||||
this.data.page.parent_id = Number(e.currentTarget.value) || null
|
||||
},
|
||||
|
||||
mediaUploaded: function(type, file) {
|
||||
if (type === 'banner') {
|
||||
this.newBanner = file
|
||||
} else {
|
||||
this.newMedia = file
|
||||
}
|
||||
},
|
||||
|
||||
fileUploaded: function(type, media) {
|
||||
this.page[type] = media
|
||||
},
|
||||
|
||||
fileRemoved: function(type) {
|
||||
this.page[type] = null
|
||||
mediaRemoved: function(type) {
|
||||
this.data.page['remove_' + type] = true
|
||||
this.data.page[type + '_prefix'] = null
|
||||
},
|
||||
|
||||
save: function(vnode, e) {
|
||||
e.preventDefault()
|
||||
if (!this.page.name) {
|
||||
if (!this.data.page.name) {
|
||||
this.error = 'Name is missing'
|
||||
} else if (!this.page.path) {
|
||||
} else if (!this.data.page.path) {
|
||||
this.error = 'Path is missing'
|
||||
} else {
|
||||
this.error = ''
|
||||
}
|
||||
if (this.error) return
|
||||
|
||||
this.page.description = vnode.state.froala ? vnode.state.froala.html.get() : this.page.description
|
||||
if (this.page.description) {
|
||||
this.page.description = this.page.description.replace(/<p[^>]+data-f-id="pbf"[^>]+>[^>]+>[^>]+>[^>]+>/, '')
|
||||
let formData = new FormData()
|
||||
if (this.newBanner) {
|
||||
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
|
||||
|
||||
let promise
|
||||
this.requestPage(
|
||||
this.editor.save()
|
||||
.then(body => {
|
||||
formData.append('content', JSON.stringify(body))
|
||||
|
||||
if (this.page.id) {
|
||||
promise = Page.updatePage(this.page.id, {
|
||||
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,
|
||||
return common.sendRequest({
|
||||
method: 'PUT',
|
||||
url: '/api/auth/pages/' + (this.lastid === 'add' ? '0' : this.lastid),
|
||||
body: formData,
|
||||
})
|
||||
} 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) {
|
||||
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)
|
||||
}
|
||||
return data
|
||||
})
|
||||
.catch(function(err) {
|
||||
vnode.state.error = err.message
|
||||
})
|
||||
.then(function() {
|
||||
vnode.state.loading = false
|
||||
m.redraw()
|
||||
})
|
||||
|
||||
return false
|
||||
)
|
||||
},
|
||||
|
||||
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})
|
||||
return (
|
||||
this.loading ?
|
||||
m('div.loading-spinner')
|
||||
: m('div.admin-wrapper', [
|
||||
m('div.admin-actions', this.page.id
|
||||
const bannerImage = this.data.page && this.data.page.banner_prefix
|
||||
? this.data.page.banner_prefix + '_large.avif'
|
||||
: null
|
||||
const mediaImage = this.data.page && this.data.page.media_prefix
|
||||
? this.data.page.media_prefix + '_large.avif'
|
||||
: 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(m.route.Link, { href: '/page/' + this.page.path }, 'View page'),
|
||||
m(m.route.Link, { href: '/admin/pages/add' }, 'Create new page'),
|
||||
m(m.route.Link, { href: '/page/' + this.data.page.path }, 'View page'),
|
||||
]
|
||||
: null),
|
||||
m('article.editpage', [
|
||||
m('header', m('h1', this.creating ? 'Create Page' : 'Edit ' + (this.page.name || '(untitled)'))),
|
||||
m('article.editarticle', [
|
||||
m('header', m('h1',
|
||||
(this.data.page.id ? 'Edit ' : 'Create Page ') + (this.data.page.name || '(untitled)')
|
||||
)
|
||||
),
|
||||
m('div.error', {
|
||||
hidden: !this.error,
|
||||
onclick: function() { vnode.state.error = '' },
|
||||
onclick: () => { vnode.state.error = '' },
|
||||
}, this.error),
|
||||
m(FileUpload, {
|
||||
onupload: this.fileUploaded.bind(this, 'banner'),
|
||||
ondelete: this.fileRemoved.bind(this, 'banner'),
|
||||
onerror: function(e) { vnode.state.error = e },
|
||||
media: this.page && this.page.banner,
|
||||
height: 300,
|
||||
onfile: this.mediaUploaded.bind(this, 'banner'),
|
||||
ondelete: this.mediaRemoved.bind(this, 'banner'),
|
||||
media: bannerImage,
|
||||
}),
|
||||
m(FileUpload, {
|
||||
class: 'cover',
|
||||
useimg: true,
|
||||
onupload: this.fileUploaded.bind(this, 'media'),
|
||||
ondelete: this.fileRemoved.bind(this, 'media'),
|
||||
onerror: function(e) { vnode.state.error = e },
|
||||
media: this.page && this.page.media,
|
||||
onfile: this.mediaUploaded.bind(this, 'media'),
|
||||
ondelete: this.mediaRemoved.bind(this, 'media'),
|
||||
media: mediaImage,
|
||||
}),
|
||||
m('form.editpage.content', {
|
||||
m('form.editarticle.content', {
|
||||
onsubmit: this.save.bind(this, vnode),
|
||||
}, [
|
||||
m('label', 'Parent'),
|
||||
m('select', {
|
||||
onchange: this.updateParent.bind(this),
|
||||
}, parents.map(function(item) {
|
||||
return m('option', { value: item.id || -1, selected: item.id === vnode.state.page.parent_id }, item.name)
|
||||
}, this.pages.filter(item => !this.data.page || item.id !== this.data.page.id).map((item) => {
|
||||
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('input', {
|
||||
type: 'text',
|
||||
value: this.page.name,
|
||||
value: this.data.page.name,
|
||||
oninput: this.updateValue.bind(this, 'name'),
|
||||
}),
|
||||
m('label', 'Description'),
|
||||
(
|
||||
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('div.input-group', [
|
||||
m('label', 'Path'),
|
||||
m('input', {
|
||||
type: 'text',
|
||||
value: this.page.path,
|
||||
value: this.data.page.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', {
|
||||
type: 'submit',
|
||||
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 common = require('../api/common')
|
||||
|
||||
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) {
|
||||
this.loading = true
|
||||
this.error = ''
|
||||
this.pages = []
|
||||
this.removePage = null
|
||||
|
||||
document.title = 'Pages - Admin NFP Moe'
|
||||
this.fetchPages(vnode)
|
||||
},
|
||||
|
||||
Page.getAllPages()
|
||||
.then(function(result) {
|
||||
vnode.state.pages = AdminPages.parseTree(result)
|
||||
fetchPages: function(vnode) {
|
||||
this.loading = true
|
||||
this.error = ''
|
||||
|
||||
return common.sendRequest({
|
||||
method: 'GET',
|
||||
url: '/api/auth/pages',
|
||||
})
|
||||
.catch(function(err) {
|
||||
vnode.state.error = err.message
|
||||
.then((result) => {
|
||||
this.pages = result.tree
|
||||
}, (err) => {
|
||||
this.error = err.message
|
||||
})
|
||||
.then(function() {
|
||||
vnode.state.loading = false
|
||||
.then(() => {
|
||||
this.loading = false
|
||||
m.redraw()
|
||||
})
|
||||
},
|
||||
|
@ -43,13 +35,21 @@ const AdminPages = {
|
|||
let removingPage = this.removePage
|
||||
this.removePage = null
|
||||
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()
|
||||
|
||||
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) {
|
||||
|
@ -63,7 +63,7 @@ const AdminPages = {
|
|||
m('td.right', page.updated_at.replace('T', ' ').split('.')[0]),
|
||||
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) {
|
||||
|
@ -79,7 +79,7 @@ const AdminPages = {
|
|||
m('header', m('h1', 'All pages')),
|
||||
m('div.error', {
|
||||
hidden: !this.error,
|
||||
onclick: function() { vnode.state.error = '' },
|
||||
onclick: () => { this.fetchPages(vnode) },
|
||||
}, this.error),
|
||||
m('table', [
|
||||
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() {
|
||||
return common.sendRequest({
|
||||
method: 'GET',
|
||||
|
|
|
@ -18,9 +18,37 @@ function 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) {
|
||||
return common.sendRequest({
|
||||
method: 'GET',
|
||||
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 {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes spinner-loader {
|
||||
|
@ -274,3 +275,72 @@ input[type="reset"]::-moz-focus-inner {
|
|||
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 Authentication = require('../authentication')
|
||||
const Fileinfo = require('../widgets/fileinfo')
|
||||
const EditorBlock = require('../widgets/editorblock')
|
||||
|
||||
const Article = {
|
||||
oninit: function(vnode) {
|
||||
|
@ -11,6 +12,10 @@ const Article = {
|
|||
this.data = {
|
||||
article: null,
|
||||
files: [],
|
||||
pictureFallback: null,
|
||||
pictureJpeg: null,
|
||||
pictureAvif: null,
|
||||
pictureCover: null,
|
||||
}
|
||||
this.showcomments = false
|
||||
|
||||
|
@ -52,6 +57,24 @@ const Article = {
|
|||
.then((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) {
|
||||
this.error = 'Article not found'
|
||||
}
|
||||
|
@ -67,19 +90,7 @@ const Article = {
|
|||
},
|
||||
|
||||
view: function(vnode) {
|
||||
var deviceWidth = window.innerWidth
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
let article = this.data.article
|
||||
return (
|
||||
this.loading ?
|
||||
m('article.article', m('div.loading-spinner'))
|
||||
|
@ -91,37 +102,53 @@ const Article = {
|
|||
},
|
||||
}, 'Article error: ' + this.error))
|
||||
: m('article.article', [
|
||||
this.data.article.page_path
|
||||
? m('div.goback', ['« ', m(m.route.Link, { href: '/page/' + this.data.article.page_path }, this.data.article.page_name)])
|
||||
article.page_path
|
||||
? m('div.goback', ['« ', m(m.route.Link, { href: '/page/' + article.page_path }, article.page_name)])
|
||||
: null,
|
||||
m('header', m('h1', this.data.article.name)),
|
||||
m('header', m('h1', article.name)),
|
||||
m('.fr-view', [
|
||||
this.data.article.media
|
||||
article.pictureFallback
|
||||
? m('a.cover', {
|
||||
rel: 'noopener',
|
||||
href: this.data.article.media.link,
|
||||
}, m('img', { src: imagePath, alt: 'Cover image for ' + this.data.article.name }))
|
||||
href: article.media_path,
|
||||
}, [
|
||||
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,
|
||||
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) {
|
||||
return m(Fileinfo, { file: file })
|
||||
}),
|
||||
m('div.entrymeta', [
|
||||
'Posted ',
|
||||
this.data.article.page_path
|
||||
article.page_path
|
||||
? [
|
||||
'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),
|
||||
' by ' + (this.data.article.admin_name || 'Admin'),
|
||||
'at ' + (article.publish_at.replace('T', ' ').split('.')[0]).substr(0, 16),
|
||||
' by ' + (article.admin_name || 'Admin'),
|
||||
]),
|
||||
]),
|
||||
Authentication.currentUser
|
||||
? m('div.admin-actions', [
|
||||
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,
|
||||
this.showcomments
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const storageName = 'logintoken'
|
||||
const storageName = 'nfp_sites_logintoken'
|
||||
|
||||
const Authentication = {
|
||||
currentUser: null,
|
||||
|
@ -37,4 +37,6 @@ const Authentication = {
|
|||
|
||||
Authentication.updateToken(localStorage.getItem(storageName))
|
||||
|
||||
window.Authentication = Authentication
|
||||
|
||||
module.exports = Authentication
|
||||
|
|
|
@ -16,7 +16,7 @@ const Footer = {
|
|||
Page.Tree.map(function(page) {
|
||||
return [
|
||||
m(m.route.Link, { class: 'root', href: '/page/' + page.path }, page.name),
|
||||
(page.children.length
|
||||
(page.children
|
||||
? m('ul', page.children.map(function(subpage) {
|
||||
return m('li', m(m.route.Link, { class: 'child', href: '/page/' + subpage.path }, subpage.name))
|
||||
}))
|
||||
|
|
|
@ -74,7 +74,7 @@ const Frontpage = {
|
|||
|
||||
view: function(vnode) {
|
||||
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) {
|
||||
var pixelRatio = window.devicePixelRatio || 1
|
||||
|
|
|
@ -9,12 +9,6 @@ m.route.set = function(path, data, options){
|
|||
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.link = function(vnode){
|
||||
m.route.linkOrig(vnode)
|
||||
|
@ -40,7 +34,6 @@ const onLoaded = function() {
|
|||
}
|
||||
|
||||
const onError = function(a, b, c) {
|
||||
console.log('onError', this, a, b, c)
|
||||
elements.forEach(function(x) { x.remove() })
|
||||
loadedAdmin = loadingAdmin = false
|
||||
loaded = 0
|
||||
|
|
|
@ -45,7 +45,6 @@ const Login = {
|
|||
if (!result.token) {
|
||||
return Promise.reject(new Error('Server authentication down.'))
|
||||
}
|
||||
console.log(result)
|
||||
Authentication.updateToken(result.token)
|
||||
m.route.set(Login.redirect || '/')
|
||||
})
|
||||
|
|
|
@ -22,11 +22,7 @@ const Menu = {
|
|||
|
||||
Menu.loading = true
|
||||
|
||||
Page.getTree()
|
||||
.then(function(results) {
|
||||
Page.Tree.splice(0, Page.Tree.length)
|
||||
Page.Tree.push.apply(Page.Tree, results)
|
||||
})
|
||||
Page.refreshTree()
|
||||
.catch(function(err) {
|
||||
Menu.error = err.message
|
||||
})
|
||||
|
@ -37,6 +33,7 @@ const Menu = {
|
|||
},
|
||||
|
||||
view: function() {
|
||||
console.log('menu view', Boolean(Authentication.currentUser))
|
||||
return [
|
||||
m('div.top', [
|
||||
m(m.route.Link,
|
||||
|
@ -73,7 +70,7 @@ const Menu = {
|
|||
class: Menu.currentActive === 'home' ? 'active' : '',
|
||||
}, 'Home'),
|
||||
Menu.loading ? m('div.loading-spinner') : Page.Tree.map(function(page) {
|
||||
if (page.children.length) {
|
||||
if (page.children) {
|
||||
return m('div.hassubmenu', [
|
||||
m(m.route.Link, {
|
||||
href: '/page/' + page.path,
|
||||
|
|
|
@ -156,7 +156,7 @@ const Page = {
|
|||
? m('aside.news', [
|
||||
m('h4', 'Latest posts under ' + this.data.page.name + ':'),
|
||||
this.data.articles.map(function(article) {
|
||||
return m(Newsentry, article)
|
||||
return m(Newsentry, { article: article })
|
||||
}),
|
||||
m(Pages, {
|
||||
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,
|
||||
m('h4', 'Latest posts under ' + this.data.page.name + ':'),
|
||||
this.data.articles.map(function(article) {
|
||||
return m(Newsentry, article)
|
||||
return m(Newsentry, { article: article })
|
||||
}),
|
||||
m(Pages, {
|
||||
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 Newsentry = {
|
||||
oninit: function(vnode) {
|
||||
this.lastId = null
|
||||
this.onbeforeupdate(vnode)
|
||||
},
|
||||
|
||||
strip: function(html) {
|
||||
var doc = new DOMParser().parseFromString(html, 'text/html')
|
||||
var out = doc.body.textContent || ''
|
||||
|
@ -11,43 +16,80 @@ const Newsentry = {
|
|||
return out
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
var deviceWidth = window.innerWidth
|
||||
var pixelRatio = window.devicePixelRatio || 1
|
||||
var imagePath = ''
|
||||
onbeforeupdate: function(vnode) {
|
||||
let article = vnode.attrs.article
|
||||
|
||||
if (vnode.attrs.media) {
|
||||
if (deviceWidth > 440 || pixelRatio <= 1) {
|
||||
imagePath = vnode.attrs.media.small_url
|
||||
if (this.lastId !== article.id) {
|
||||
this.lastId = article.id
|
||||
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 {
|
||||
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', [
|
||||
imagePath
|
||||
this.pictureFallback
|
||||
? m(m.route.Link, {
|
||||
class: 'cover',
|
||||
href: '/article/' + vnode.attrs.path,
|
||||
}, m('picture', [
|
||||
m('source', { srcset:
|
||||
vnode.attrs.media.small_url + ''
|
||||
href: '/article/' + article.path,
|
||||
},
|
||||
m('picture', [
|
||||
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('div.entrycontent', [
|
||||
m('div.title', [
|
||||
m(m.route.Link,
|
||||
{ href: '/article/' + vnode.attrs.path },
|
||||
m('h3', [vnode.attrs.name])
|
||||
{ href: '/article/' + article.path },
|
||||
m('h3', [article.name])
|
||||
),
|
||||
]),
|
||||
(vnode.attrs.files && vnode.attrs.files.length
|
||||
? vnode.attrs.files.map(function(file) {
|
||||
(article.files && article.files.length
|
||||
? article.files.map(function(file) {
|
||||
return m(Fileinfo, { file: file, slim: true })
|
||||
})
|
||||
: vnode.attrs.description
|
||||
? m('span.entrydescription', Newsentry.strip(vnode.attrs.description))
|
||||
: this.description
|
||||
? m('span.entrydescription', this.description)
|
||||
: null),
|
||||
]),
|
||||
])
|
||||
|
|
|
@ -1,25 +1,36 @@
|
|||
const Fileinfo = require('./fileinfo')
|
||||
const EditorBlock = require('./editorblock')
|
||||
|
||||
const Newsitem = {
|
||||
oninit: function(vnode) {
|
||||
let article = vnode.attrs.article
|
||||
if (article.media_prefix) {
|
||||
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.lastId = null
|
||||
this.onbeforeupdate(vnode)
|
||||
},
|
||||
|
||||
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, '
|
||||
+ '400px'
|
||||
} else {
|
||||
this.fallbackImage = null
|
||||
this.srcsetJpeg = null
|
||||
this.srcsetAvif = null
|
||||
this.coverSizes = null
|
||||
this.pictureFallback = null
|
||||
this.pictureJpeg = null
|
||||
this.pictureAvif = null
|
||||
this.pictureCover = null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -32,32 +43,30 @@ const Newsitem = {
|
|||
m('h3', [article.name])
|
||||
),
|
||||
m('div.newsitemcontent', [
|
||||
this.fallbackImage
|
||||
this.pictureFallback
|
||||
? m(m.route.Link, {
|
||||
class: 'cover',
|
||||
href: '/article/' + article.path,
|
||||
},
|
||||
m('picture', [
|
||||
this.srcsetAvif ? m('source', {
|
||||
srcset: this.srcsetAvif,
|
||||
sizes: this.coverSizes,
|
||||
m('source', {
|
||||
srcset: this.pictureAvif,
|
||||
sizes: this.pictureCover,
|
||||
type: 'image/avif',
|
||||
}) : null,
|
||||
}),
|
||||
m('img', {
|
||||
srcset: this.srcsetJpeg,
|
||||
sizes: this.coverSizes,
|
||||
srcset: this.pictureJpeg,
|
||||
sizes: this.pictureCover,
|
||||
alt: 'Image for news item ' + article.name,
|
||||
src: this.fallbackImage,
|
||||
src: this.pictureFallback,
|
||||
}),
|
||||
])
|
||||
)
|
||||
: null,
|
||||
m('div.entrycontent', {
|
||||
class: article.media ? '' : 'extrapadding',
|
||||
}, [
|
||||
(article.content
|
||||
? m('.fr-view', m.trust(article.content))
|
||||
: null),
|
||||
m('div.entrycontent', [
|
||||
article.content.blocks.map(block => {
|
||||
return m(EditorBlock, { block: block })
|
||||
}),
|
||||
(article.files && article.files.length
|
||||
? article.files.map(function(file) {
|
||||
return m(Fileinfo, { file: file, trim: true })
|
||||
|
|
Loading…
Reference in a new issue