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