12 changed files with 221 additions and 290 deletions
@ -0,0 +1,111 @@ |
|||
// admin/link.js
|
|||
// Copyright (C) 2021 Digital Telepresence, LLC
|
|||
// License: Apache-2.0
|
|||
|
|||
'use strict'; |
|||
|
|||
const DTP_COMPONENT_NAME = 'admin:link'; |
|||
const express = require('express'); |
|||
|
|||
const { SiteController, SiteError } = require('../../../lib/site-lib'); |
|||
|
|||
class LinkController extends SiteController { |
|||
|
|||
constructor (dtp) { |
|||
super(dtp, DTP_COMPONENT_NAME); |
|||
} |
|||
|
|||
async start ( ) { |
|||
const router = express.Router(); |
|||
router.use(async (req, res, next) => { |
|||
res.locals.currentView = 'admin'; |
|||
res.locals.adminView = 'link'; |
|||
return next(); |
|||
}); |
|||
|
|||
router.param('linkId', this.populateLinkId.bind(this)); |
|||
|
|||
router.post('/:linkId', this.postUpdateLink.bind(this)); |
|||
|
|||
router.get('/:linkId', this.getLinkView.bind(this)); |
|||
router.get('/', this.getIndex.bind(this)); |
|||
|
|||
router.delete('/:linkId', this.deleteLink.bind(this)); |
|||
|
|||
return router; |
|||
} |
|||
|
|||
async populateLinkId (req, res, next, linkId) { |
|||
const { link: linkService } = this.dtp.services; |
|||
try { |
|||
res.locals.link = await linkService.getById(linkId); |
|||
if (!res.locals.link) { |
|||
throw new SiteError(404, 'Link not found'); |
|||
} |
|||
return next(); |
|||
} catch (error) { |
|||
this.log.error('failed to populate linkId', { linkId, error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async postUpdateLink (req, res, next) { |
|||
const { link: linkService } = this.dtp.services; |
|||
try { |
|||
await linkService.update(res.locals.link, req.body); |
|||
res.redirect('/admin/link'); |
|||
} catch (error) { |
|||
this.log.error('failed to update link', { linkId: res.locals.link._id, error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async getLinkView (req, res) { |
|||
res.render('admin/link/view'); |
|||
} |
|||
|
|||
async getIndex (req, res, next) { |
|||
const { link: linkService } = this.dtp.services; |
|||
try { |
|||
res.locals.totalLinkCount = await linkService.getTotalCount(); |
|||
res.locals.pagination = this.getPaginationParameters(req, 50); |
|||
res.locals.links = await linkService.getAdmin(res.locals.pagination); |
|||
res.render('admin/link/index'); |
|||
} catch (error) { |
|||
this.log.error('failed to fetch links', { error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async deleteLink (req, res) { |
|||
const { link: linkService, displayEngine: displayEngineService } = this.dtp.services; |
|||
try { |
|||
const displayList = displayEngineService.createDisplayList('delete-link'); |
|||
|
|||
await linkService.remove(res.locals.link); |
|||
|
|||
displayList.removeElement(`li[data-link-id="${res.locals.link._id}"]`); |
|||
displayList.showNotification( |
|||
`Link "${res.locals.link.title}" deleted`, |
|||
'success', |
|||
'bottom-center', |
|||
3000, |
|||
); |
|||
res.status(200).json({ success: true, displayList }); |
|||
} catch (error) { |
|||
this.log.error('failed to delete link', { |
|||
linkId: res.local.link._id, |
|||
error, |
|||
}); |
|||
res.status(error.statusCode || 500).json({ |
|||
success: false, |
|||
message: error.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = async (dtp) => { |
|||
let controller = new LinkController(dtp); |
|||
return controller; |
|||
}; |
@ -1,135 +0,0 @@ |
|||
// admin/page.js
|
|||
// Copyright (C) 2021 Digital Telepresence, LLC
|
|||
// License: Apache-2.0
|
|||
|
|||
'use strict'; |
|||
|
|||
const DTP_COMPONENT_NAME = 'admin:page'; |
|||
const express = require('express'); |
|||
|
|||
const { SiteController, SiteError } = require('../../../lib/site-lib'); |
|||
|
|||
class PageController extends SiteController { |
|||
|
|||
constructor (dtp) { |
|||
super(dtp, DTP_COMPONENT_NAME); |
|||
} |
|||
|
|||
async start ( ) { |
|||
const router = express.Router(); |
|||
router.use(async (req, res, next) => { |
|||
res.locals.currentView = 'admin'; |
|||
res.locals.adminView = 'page'; |
|||
return next(); |
|||
}); |
|||
|
|||
router.param('pageId', this.populatePageId.bind(this)); |
|||
|
|||
router.post('/:pageId', this.pageUpdatePage.bind(this)); |
|||
router.post('/', this.pageCreatePage.bind(this)); |
|||
|
|||
router.get('/compose', this.getComposer.bind(this)); |
|||
router.get('/:pageId', this.getComposer.bind(this)); |
|||
|
|||
router.get('/', this.getIndex.bind(this)); |
|||
|
|||
router.delete('/:pageId', this.deletePage.bind(this)); |
|||
|
|||
return router; |
|||
} |
|||
|
|||
async populatePageId (req, res, next, pageId) { |
|||
const { page: pageService } = this.dtp.services; |
|||
try { |
|||
res.locals.page = await pageService.getById(pageId); |
|||
if (!res.locals.page) { |
|||
throw new SiteError(404, 'Page not found'); |
|||
} |
|||
return next(); |
|||
} catch (error) { |
|||
this.log.error('failed to populate pageId', { pageId, error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async pageUpdatePage (req, res, next) { |
|||
const { page: pageService } = this.dtp.services; |
|||
try { |
|||
await pageService.update(res.locals.page, req.body); |
|||
res.redirect('/admin/page'); |
|||
} catch (error) { |
|||
this.log.error('failed to update page', { newletterId: res.locals.page._id, error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async pageCreatePage (req, res, next) { |
|||
const { page: pageService } = this.dtp.services; |
|||
try { |
|||
await pageService.create(req.user, req.body); |
|||
res.redirect('/admin/page'); |
|||
} catch (error) { |
|||
this.log.error('failed to create page', { error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async getComposer (req, res, next) { |
|||
const { page: pageService } = this.dtp.services; |
|||
try { |
|||
let excludedPages; |
|||
if (res.locals.page) { |
|||
excludedPages = [res.locals.page._id]; |
|||
} |
|||
res.locals.availablePages = await pageService.getAvailablePages(excludedPages); |
|||
res.render('admin/page/editor'); |
|||
} catch (error) { |
|||
this.log.error('failed to serve page editor', { error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async getIndex (req, res, next) { |
|||
const { page: pageService } = this.dtp.services; |
|||
try { |
|||
res.locals.pagination = this.getPaginationParameters(req, 20); |
|||
res.locals.pages = await pageService.getPages(res.locals.pagination, ['draft', 'published', 'archived']); |
|||
res.render('admin/page/index'); |
|||
} catch (error) { |
|||
this.log.error('failed to fetch pages', { error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async deletePage (req, res) { |
|||
const { page: pageService, displayEngine: displayEngineService } = this.dtp.services; |
|||
try { |
|||
const displayList = displayEngineService.createDisplayList('delete-page'); |
|||
|
|||
await pageService.deletePage(res.locals.page); |
|||
|
|||
displayList.removeElement(`li[data-page-id="${res.locals.page._id}"]`); |
|||
displayList.showNotification( |
|||
`Page "${res.locals.page.title}" deleted`, |
|||
'success', |
|||
'bottom-center', |
|||
3000, |
|||
); |
|||
res.status(200).json({ success: true, displayList }); |
|||
} catch (error) { |
|||
this.log.error('failed to delete page', { |
|||
pageId: res.local.page._id, |
|||
error, |
|||
}); |
|||
res.status(error.statusCode || 500).json({ |
|||
success: false, |
|||
message: error.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = async (dtp) => { |
|||
let controller = new PageController(dtp); |
|||
return controller; |
|||
}; |
@ -1,124 +0,0 @@ |
|||
// admin/post.js
|
|||
// Copyright (C) 2021 Digital Telepresence, LLC
|
|||
// License: Apache-2.0
|
|||
|
|||
'use strict'; |
|||
|
|||
const DTP_COMPONENT_NAME = 'admin:post'; |
|||
const express = require('express'); |
|||
|
|||
const { SiteController, SiteError } = require('../../../lib/site-lib'); |
|||
|
|||
class PostController extends SiteController { |
|||
|
|||
constructor (dtp) { |
|||
super(dtp, DTP_COMPONENT_NAME); |
|||
} |
|||
|
|||
async start ( ) { |
|||
const router = express.Router(); |
|||
router.use(async (req, res, next) => { |
|||
res.locals.currentView = 'admin'; |
|||
res.locals.adminView = 'post'; |
|||
return next(); |
|||
}); |
|||
|
|||
router.param('postId', this.populatePostId.bind(this)); |
|||
|
|||
router.post('/:postId', this.postUpdatePost.bind(this)); |
|||
router.post('/', this.postCreatePost.bind(this)); |
|||
|
|||
router.get('/compose', this.getComposer.bind(this)); |
|||
router.get('/:postId', this.getComposer.bind(this)); |
|||
|
|||
router.get('/', this.getIndex.bind(this)); |
|||
|
|||
router.delete('/:postId', this.deletePost.bind(this)); |
|||
|
|||
return router; |
|||
} |
|||
|
|||
async populatePostId (req, res, next, postId) { |
|||
const { post: postService } = this.dtp.services; |
|||
try { |
|||
res.locals.post = await postService.getById(postId); |
|||
if (!res.locals.post) { |
|||
throw new SiteError(404, 'Post not found'); |
|||
} |
|||
return next(); |
|||
} catch (error) { |
|||
this.log.error('failed to populate postId', { postId, error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async postUpdatePost (req, res, next) { |
|||
const { post: postService } = this.dtp.services; |
|||
try { |
|||
await postService.update(res.locals.post, req.body); |
|||
res.redirect('/admin/post'); |
|||
} catch (error) { |
|||
this.log.error('failed to update post', { newletterId: res.locals.post._id, error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async postCreatePost (req, res, next) { |
|||
const { post: postService } = this.dtp.services; |
|||
try { |
|||
await postService.create(req.user, req.body); |
|||
res.redirect('/admin/post'); |
|||
} catch (error) { |
|||
this.log.error('failed to create post', { error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async getComposer (req, res) { |
|||
res.render('admin/post/editor'); |
|||
} |
|||
|
|||
async getIndex (req, res, next) { |
|||
const { post: postService } = this.dtp.services; |
|||
try { |
|||
res.locals.pagination = this.getPaginationParameters(req, 20); |
|||
res.locals.posts = await postService.getPosts(res.locals.pagination, ['draft', 'published', 'archived']); |
|||
res.render('admin/post/index'); |
|||
} catch (error) { |
|||
this.log.error('failed to fetch posts', { error }); |
|||
return next(error); |
|||
} |
|||
} |
|||
|
|||
async deletePost (req, res) { |
|||
const { post: postService, displayEngine: displayEngineService } = this.dtp.services; |
|||
try { |
|||
const displayList = displayEngineService.createDisplayList('delete-post'); |
|||
|
|||
await postService.deletePost(res.locals.post); |
|||
|
|||
displayList.removeElement(`li[data-post-id="${res.locals.post._id}"]`); |
|||
displayList.showNotification( |
|||
`Post "${res.locals.post.title}" deleted`, |
|||
'success', |
|||
'bottom-center', |
|||
3000, |
|||
); |
|||
res.status(200).json({ success: true, displayList }); |
|||
} catch (error) { |
|||
this.log.error('failed to delete post', { |
|||
postId: res.local.post._id, |
|||
error, |
|||
}); |
|||
res.status(error.statusCode || 500).json({ |
|||
success: false, |
|||
message: error.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = async (dtp) => { |
|||
let controller = new PostController(dtp); |
|||
return controller; |
|||
}; |
@ -0,0 +1,26 @@ |
|||
extends ../layouts/main |
|||
block content |
|||
|
|||
include ../../components/pagination-bar |
|||
|
|||
.uk-margin |
|||
if Array.isArray(links) && (links.length > 0) |
|||
ul.uk-list |
|||
each link in links |
|||
li |
|||
div(uk-grid).uk-grid-small.uk-flex-middle |
|||
.uk-width-expand |
|||
div |
|||
span.uk-margin-small-right= link.label |
|||
span.uk-margin-small-right #[a(href= `/${link.user.username}`).uk-text-truncate= link.user.username] |
|||
a(href= link.href)= link.href |
|||
.uk-width-auto |
|||
form(method="POST", action=`/admin/link/${link._id}`, onsubmit="").uk-form |
|||
button(type="submit").uk-button.dtp-button-danger.uk-button-small |
|||
span |
|||
i.fas.fa-trash |
|||
else |
|||
div There are no links |
|||
|
|||
.uk-margin |
|||
+renderPaginationBar('/admin/link', totalLinkCount) |
Loading…
Reference in new issue