diff --git a/app/controllers/admin.js b/app/controllers/admin.js index 3800970..cad0613 100644 --- a/app/controllers/admin.js +++ b/app/controllers/admin.js @@ -51,6 +51,7 @@ class AdminController extends SiteController { router.use('/log', await this.loadChild(path.join(__dirname, 'admin', 'log'))); router.use('/newsletter', await this.loadChild(path.join(__dirname, 'admin', 'newsletter'))); router.use('/newsroom', await this.loadChild(path.join(__dirname, 'admin', 'newsroom'))); + router.use('/otp', await this.loadChild(path.join(__dirname, 'admin', 'otp'))); router.use('/page', await this.loadChild(path.join(__dirname, 'admin', 'page'))); router.use('/post', await this.loadChild(path.join(__dirname, 'admin', 'post'))); router.use('/settings', await this.loadChild(path.join(__dirname, 'admin', 'settings'))); diff --git a/app/controllers/admin/otp.js b/app/controllers/admin/otp.js new file mode 100644 index 0000000..f80d6df --- /dev/null +++ b/app/controllers/admin/otp.js @@ -0,0 +1,60 @@ +// admin/otp.js +// Copyright (C) 2021 Digital Telepresence, LLC +// License: Apache-2.0 + +'use strict'; + +const express = require('express'); +// const multer = require('multer'); + +const { SiteController, SiteError } = require('../../../lib/site-lib'); + +class OtpAdminController extends SiteController { + + constructor (dtp) { + super(dtp, module.exports); + dtp.log.debug("OTP controller started"); + } + + async start ( ) { + // const upload = multer({ dest: `/tmp/${this.dtp.config.site.domainKey}/uploads/${module.exports.slug}` }); + + const router = express.Router(); + router.use(async (req, res, next) => { + res.locals.currentView = 'admin'; + res.locals.adminView = 'otp'; + return next(); + }); + + // router.param('otp', this.populateOtp.bind(this)); + + + + router.get('/', this.getIndex.bind(this)); + + // router.delete('/:postId', this.deletePost.bind(this)); + + return router; + } + + async getIndex (req, res, next) { + try { + const { otpAuth: otpAuthService } = this.dtp.services; + if (!req.user) { + throw new SiteError(402, "Error getting user"); + } + res.locals.tokens = await otpAuthService.getBackupTokens(req.user, "Admin"); + res.render('admin/otp/index'); + } catch (error) { + this.log.error('failed to get tokens', { error }); + return next(error); + } + } + +} + +module.exports = { + name: 'adminOtp', + slug: 'admin-opt', + create: async (dtp) => { return new OtpAdminController(dtp); }, +}; \ No newline at end of file diff --git a/app/controllers/admin/post.js b/app/controllers/admin/post.js index d412d95..0c6b41f 100644 --- a/app/controllers/admin/post.js +++ b/app/controllers/admin/post.js @@ -89,6 +89,10 @@ class PostController extends SiteController { } async getComposer (req, res) { + const { post: postService } = this.dtp.services; + if (!res.locals.post) { + res.locals.post = await postService.createPlaceholder(req.user); + } res.render('post/editor'); } diff --git a/app/controllers/post.js b/app/controllers/post.js index 10ac01a..0fa81cc 100644 --- a/app/controllers/post.js +++ b/app/controllers/post.js @@ -30,6 +30,9 @@ class PostController extends SiteController { dtp.app.use('/post', router); async function requireAuthorPrivileges (req, res, next) { + if (req.user && req.user.flags.isAdmin) { + return next(); + } if (!req.user || !req.user.permissions.canAuthorPages) { return next(new SiteError(403, 'Author privileges are required')); } @@ -190,9 +193,11 @@ class PostController extends SiteController { async postUpdatePost (req, res, next) { const { post: postService } = this.dtp.services; try { - if (!req.user._id.equals(res.locals.post.author._id) && - !req.user.permissions.canPublishPosts) { - throw new SiteError(403, 'This is not your post'); + if(!req.user.flags.isAdmin){ + if (!req.user._id.equals(res.locals.post.author._id) || + !req.user.permissions.canPublishPosts) { + throw new SiteError(403, 'This is not your post'); + } } await postService.update(req.user, res.locals.post, req.body); res.redirect(`/post/${res.locals.post.slug}`); @@ -282,6 +287,10 @@ class PostController extends SiteController { res.locals.pagination, ); res.locals.pageTitle = `${res.locals.post.title} on ${this.dtp.config.site.name}`; + res.locals.pageDescription = `${res.locals.post.summary}`; + if (res.locals.post.image) { + res.locals.shareImage = `https://${this.dtp.config.site.domain}/image/${res.locals.post.image._id}`; + } res.render('post/view'); } catch (error) { this.log.error('failed to service post view', { postId: res.locals.post._id, error }); @@ -318,15 +327,17 @@ class PostController extends SiteController { async deletePost (req, res) { const { post: postService } = this.dtp.services; try { - if (!req.user._id.equals(res.locals.post.author._id) || - !req.user.permissions.canPublishPosts) { - throw new SiteError(403, 'This is not your post'); + // only give admins and the author permission to delete + if (!req.user.flags.isAdmin) { + if (!req.user._id.equals(res.locals.post.author._id)) { + throw new SiteError(403, 'This is not your post'); + } } await postService.deletePost(res.locals.post); const displayList = this.createDisplayList('add-recipient'); - displayList.reload(); + displayList.navigateTo('/'); res.status(200).json({ success: true, displayList }); } catch (error) { diff --git a/app/models/user.js b/app/models/user.js index 63d5d72..7a55793 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -53,10 +53,9 @@ UserSchema.virtual('hasPublishPermissions').get( function ( ) { }); UserSchema.virtual('hasAuthorDashboard').get( function ( ) { - return this.permissions.canAuthorPages || - this.permissions.cahAuthorPosts || - this.permissions.canPublishPages || - this.permissions.canPublishPosts; + return this.permissions.cahAuthorPosts || + this.permissions.canPublishPosts || + this.flags.isAdmin; }); module.exports = mongoose.model('User', UserSchema); diff --git a/app/services/otp-auth.js b/app/services/otp-auth.js index 85fb0b4..4e93318 100644 --- a/app/services/otp-auth.js +++ b/app/services/otp-auth.js @@ -220,6 +220,13 @@ class OtpAuthService extends SiteService { async removeForUser (user) { return await OtpAccount.deleteMany({ user: user }); } + + async getBackupTokens (user, serviceName) { + const tokens = await OtpAccount.findOne({ user: user._id, service: serviceName }) + .select('+backupTokens') + .lean(); + return tokens.backupTokens; + } } module.exports = { diff --git a/app/services/page.js b/app/services/page.js index c460b3e..bf20e2c 100644 --- a/app/services/page.js +++ b/app/services/page.js @@ -49,6 +49,27 @@ class PageService extends SiteService { } } + async createPlaceholder (author) { + const NOW = new Date(); + + if (!author.permissions.canAuthorPages || !author.flags.isAdmin) { + throw new SiteError(403, 'You are not permitted to author pages'); + } + + let page = new Page(); + page.created = NOW; + page.authorType = author.type; + page.author = author._id; + page.title = "New Draft page"; + page.slug = `draft-page-${page._id}`; + await page.save(); + + page = page.toObject(); + page.author = author; // self-populate instead of calling db + + return page; + } + async create (author, pageDefinition) { if (!author.permissions.canAuthorPages) { throw new SiteError(403, 'You are not permitted to author pages'); diff --git a/app/services/post.js b/app/services/post.js index d2e1b8c..4913529 100644 --- a/app/services/post.js +++ b/app/services/post.js @@ -42,7 +42,7 @@ class PostService extends SiteService { async createPlaceholder (author) { const NOW = new Date(); - if (!author.permissions.canAuthorPosts) { + if (!author.permissions.canAuthorPosts || !author.flags.isAdmin) { throw new SiteError(403, 'You are not permitted to author posts'); } @@ -63,11 +63,13 @@ class PostService extends SiteService { async create (author, postDefinition) { const NOW = new Date(); - if (!author.permissions.canAuthorPosts) { - throw new SiteError(403, 'You are not permitted to author posts'); - } - if ((postDefinition.status === 'published') && !author.permissions.canPublishPosts) { - throw new SiteError(403, 'You are not permitted to publish posts'); + if (!author.flags.isAdmin){ + if (!author.permissions.canAuthorPosts) { + throw new SiteError(403, 'You are not permitted to author posts'); + } + if ((postDefinition.status === 'published') && !author.permissions.canPublishPosts) { + throw new SiteError(403, 'You are not permitted to publish posts'); + } } const post = new Post(); @@ -92,10 +94,11 @@ class PostService extends SiteService { async update (user, post, postDefinition) { const { coreNode: coreNodeService } = this.dtp.services; - if (!user.permissions.canAuthorPosts) { - throw new SiteError(403, 'You are not permitted to author posts'); + if (!user.flags.isAdmin){ + if (!user.permissions.canAuthorPosts) { + throw new SiteError(403, 'You are not permitted to author posts'); + } } - const NOW = new Date(); const updateOp = { $setOnInsert: { diff --git a/app/views/admin/components/menu.pug b/app/views/admin/components/menu.pug index 54df834..476f37f 100644 --- a/app/views/admin/components/menu.pug +++ b/app/views/admin/components/menu.pug @@ -13,6 +13,12 @@ ul(uk-nav).uk-nav-default i.fas.fa-cog span.uk-margin-small-left Settings + li(class={ 'uk-active': (adminView === 'otp') }) + a(href="/admin/otp") + span.nav-item-icon + i.fas.fa-cog + span.uk-margin-small-left Otp Tokens + li.uk-nav-divider li(class={ 'uk-active': (adminView === 'announcement') }) diff --git a/app/views/admin/otp/index.pug b/app/views/admin/otp/index.pug new file mode 100644 index 0000000..99a7079 --- /dev/null +++ b/app/views/admin/otp/index.pug @@ -0,0 +1,9 @@ +extends ../layouts/main +block content + + div(uk-grid) + .uk-width-expand + h1 Tokens + + each token of tokens + p #{token.token} \ No newline at end of file diff --git a/app/views/components/social-card/facebook.pug b/app/views/components/social-card/facebook.pug index d3a7432..a72fcf9 100644 --- a/app/views/components/social-card/facebook.pug +++ b/app/views/components/social-card/facebook.pug @@ -1,7 +1,7 @@ block facebook-card meta(property='og:site_name', content= site.name) meta(property='og:type', content='website') - meta(property='og:image', content= `https://${site.domain}/img/social-cards/${site.domainKey}.png?v=${pkg.version}`) + meta(property='og:image', content= shareImage || `https://${site.domain}/img/social-cards/${site.domainKey}.png?v=${pkg.version}`) meta(property='og:url', content= `https://${site.domain}${request.url}`) meta(property='og:title', content= pageTitle || site.name) meta(property='og:description', content= pageDescription || site.description) diff --git a/app/views/components/social-card/twitter.pug b/app/views/components/social-card/twitter.pug index f3aab08..6002338 100644 --- a/app/views/components/social-card/twitter.pug +++ b/app/views/components/social-card/twitter.pug @@ -1,5 +1,5 @@ block twitter-card meta(name='twitter:card', content='summary_large_image') - meta(name='twitter:image' content= `https://${site.domain}/img/social-cards/${site.domainKey}.png?v=${pkg.version}`) + meta(name='twitter:image' content= shareImage || `https://${site.domain}/img/social-cards/${site.domainKey}.png?v=${pkg.version}`) meta(name='twitter:title', content= pageTitle || site.name) meta(name='twitter:description', content= pageDescription || site.description) diff --git a/app/views/post/view.pug b/app/views/post/view.pug index 1994378..ae9f140 100644 --- a/app/views/post/view.pug +++ b/app/views/post/view.pug @@ -33,7 +33,7 @@ block content if user && user.hasAuthorDashboard .uk-width-auto= post.status - if post.author._id.equals(user._id) || user.permissions.canPublishPosts + if post.author._id.equals(user._id) || user.hasAuthorDashboard .uk-width-auto a(href=`/post/${post._id}/edit`).uk-display-block +renderButtonIcon('fa-pen', 'edit') diff --git a/client/img/icon/dev.themachine-server.xyz/icon-114x114.png b/client/img/icon/dev.themachine-server.xyz/icon-114x114.png new file mode 100644 index 0000000..ad240e9 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-114x114.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-120x120.png b/client/img/icon/dev.themachine-server.xyz/icon-120x120.png new file mode 100644 index 0000000..93cf8e1 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-120x120.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-144x144.png b/client/img/icon/dev.themachine-server.xyz/icon-144x144.png new file mode 100644 index 0000000..dfda967 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-144x144.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-150x150.png b/client/img/icon/dev.themachine-server.xyz/icon-150x150.png new file mode 100644 index 0000000..853ab8b Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-150x150.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-152x152.png b/client/img/icon/dev.themachine-server.xyz/icon-152x152.png new file mode 100644 index 0000000..743e2f9 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-152x152.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-16x16.png b/client/img/icon/dev.themachine-server.xyz/icon-16x16.png new file mode 100644 index 0000000..0e2bb29 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-16x16.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-180x180.png b/client/img/icon/dev.themachine-server.xyz/icon-180x180.png new file mode 100644 index 0000000..3e7d7e1 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-180x180.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-192x192.png b/client/img/icon/dev.themachine-server.xyz/icon-192x192.png new file mode 100644 index 0000000..4c981ce Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-192x192.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-256x256.png b/client/img/icon/dev.themachine-server.xyz/icon-256x256.png new file mode 100644 index 0000000..80f43d0 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-256x256.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-310x310.png b/client/img/icon/dev.themachine-server.xyz/icon-310x310.png new file mode 100644 index 0000000..c0bfd48 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-310x310.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-32x32.png b/client/img/icon/dev.themachine-server.xyz/icon-32x32.png new file mode 100644 index 0000000..363d109 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-32x32.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-36x36.png b/client/img/icon/dev.themachine-server.xyz/icon-36x36.png new file mode 100644 index 0000000..e8a35f6 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-36x36.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-384x384.png b/client/img/icon/dev.themachine-server.xyz/icon-384x384.png new file mode 100644 index 0000000..6792543 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-384x384.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-48x48.png b/client/img/icon/dev.themachine-server.xyz/icon-48x48.png new file mode 100644 index 0000000..5300718 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-48x48.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-512x512.png b/client/img/icon/dev.themachine-server.xyz/icon-512x512.png new file mode 100644 index 0000000..bee68f1 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-512x512.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-57x57.png b/client/img/icon/dev.themachine-server.xyz/icon-57x57.png new file mode 100644 index 0000000..121933e Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-57x57.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-60x60.png b/client/img/icon/dev.themachine-server.xyz/icon-60x60.png new file mode 100644 index 0000000..c4d94bf Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-60x60.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-70x70.png b/client/img/icon/dev.themachine-server.xyz/icon-70x70.png new file mode 100644 index 0000000..7bc1b4b Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-70x70.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-72x72.png b/client/img/icon/dev.themachine-server.xyz/icon-72x72.png new file mode 100644 index 0000000..443227a Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-72x72.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-76x76.png b/client/img/icon/dev.themachine-server.xyz/icon-76x76.png new file mode 100644 index 0000000..3cf9747 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-76x76.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon-96x96.png b/client/img/icon/dev.themachine-server.xyz/icon-96x96.png new file mode 100644 index 0000000..f660535 Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon-96x96.png differ diff --git a/client/img/icon/dev.themachine-server.xyz/icon.png b/client/img/icon/dev.themachine-server.xyz/icon.png new file mode 100644 index 0000000..bd4951a Binary files /dev/null and b/client/img/icon/dev.themachine-server.xyz/icon.png differ