From 88ce70dcaf1e38b8119df98169decae2146e048e Mon Sep 17 00:00:00 2001 From: Andrew Woodlee Date: Sun, 6 Nov 2022 21:03:03 -0600 Subject: [PATCH 1/4] added publisher access to author dashboard --- app/controllers/author.js | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/app/controllers/author.js b/app/controllers/author.js index 30e6984..0b3ff1d 100644 --- a/app/controllers/author.js +++ b/app/controllers/author.js @@ -21,19 +21,32 @@ class AuthorController extends SiteController { const { author: authorLimiter } = limiterService.config; // const upload = multer({ dest: `/tmp/dtp-sites/${this.dtp.config.site.domainKey}`}); + async function checkPermissions(req, res) { + + if (!req.user){ + return false + } + const canAuthor = req.user.permissions.canAuthorPages || req.user.permissions.canAuthorPosts; + const canPublish = req.user.permissions.canPublishPages || req.user.permissions.canPublishPosts; + if (!canAuthor || !canPublish) { + return false; + } + return true; + } + + const router = express.Router(); dtp.app.use('/author', router); - router.use( - async (req, res, next) => { - res.locals.currentView = 'author'; - if (!req.user || !req.user.permissions.canAuthorPages) { + router.use(async (req, res, next) => { + res.locals.currentView = 'author'; + const canAuthorOrPublish = await checkPermissions(req, res); + if (!canAuthorOrPublish) { return next(new SiteError(403, 'Author privileges are required')); - } - return next(); - }, - ); + } + return next(); + }); router.get('/post', limiterService.createMiddleware(authorLimiter.getPostIndex), From e02caa4090c14702ec3a7335ec85c262dcc3ff30 Mon Sep 17 00:00:00 2001 From: Andrew Woodlee Date: Sun, 6 Nov 2022 21:09:50 -0600 Subject: [PATCH 2/4] fix typo --- app/controllers/author.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/author.js b/app/controllers/author.js index 0b3ff1d..ad5bd5b 100644 --- a/app/controllers/author.js +++ b/app/controllers/author.js @@ -28,7 +28,7 @@ class AuthorController extends SiteController { } const canAuthor = req.user.permissions.canAuthorPages || req.user.permissions.canAuthorPosts; const canPublish = req.user.permissions.canPublishPages || req.user.permissions.canPublishPosts; - if (!canAuthor || !canPublish) { + if (!canAuthor && !canPublish) { return false; } return true; From 4df5fc8e0e9f358bf9ac2f8e7cbb9bdff1e793ca Mon Sep 17 00:00:00 2001 From: Andrew Woodlee Date: Mon, 7 Nov 2022 00:56:37 -0600 Subject: [PATCH 3/4] added better view of comments in author dashboard --- app/views/author/index.pug | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/app/views/author/index.pug b/app/views/author/index.pug index 150f36c..23c10f9 100644 --- a/app/views/author/index.pug +++ b/app/views/author/index.pug @@ -21,20 +21,24 @@ block content span.uk-margin-small-left.uk-text-bold Create Post div(uk-grid) - div(class="uk-width-1-1 uk-width-2-3@m") - +renderSectionTitle('Recent Comments', { url: '/author/comments', title: 'See All', label: 'SEE ALL' }) - - .content-block - .uk-margin - ul#post-comment-list.uk-list.uk-list-divider.uk-list-large.uk-margin - each comment in authorComments.comments - li - .uk-margin - a(href=`/post/${comment.resource.slug}`).uk-display-block.uk-link-reset - +renderPostSummary(comment.resource) - +renderComment(comment, { }) - - +renderPaginationBar('/author', published.totalPostCount) + if authorComments.comments.length > 0 + div(class="uk-width-1-1 uk-width-2-3@m") + +renderSectionTitle('Recent Comments', { url: '/author/comments', title: 'See All', label: 'SEE ALL' }) + .content-block + .uk-margin + ul#post-comment-list.uk-list.uk-list-divider.uk-list-large.uk-margin + each comment in authorComments.comments + li + .uk-margin + a(href=`/post/${comment.resource.slug}`).uk-display-block.uk-link-reset + +renderPostSummary(comment.resource) + +renderComment(comment, { }) + + +renderPaginationBar('/author', published.totalPostCount) + else + div(class="uk-width-1-1 uk-width-2-3@m") + +renderSectionTitle('Comments') + div There are no comments. div(class="uk-width-1-1 uk-width-1-3@m") .uk-margin From 96d242b217b9356567b0f039dad447e643c3705f Mon Sep 17 00:00:00 2001 From: Andrew Woodlee Date: Tue, 8 Nov 2022 04:21:45 -0600 Subject: [PATCH 4/4] Added author and publisher view, made posts editable by publishers Gave author dashboard views for different permissions. Made editing by publishers work. --- app/controllers/admin/site-link.js | 2 +- app/controllers/author.js | 55 +++++++----- app/controllers/post.js | 6 +- app/views/author/index.pug | 131 ++++++++++++++++++++--------- app/views/otp/welcome.pug | 2 +- app/views/post/view.pug | 2 +- app/views/welcome/signup.pug | 2 +- 7 files changed, 136 insertions(+), 64 deletions(-) diff --git a/app/controllers/admin/site-link.js b/app/controllers/admin/site-link.js index 1a4d534..373ad5b 100644 --- a/app/controllers/admin/site-link.js +++ b/app/controllers/admin/site-link.js @@ -18,7 +18,7 @@ class SiteLinkAdminController extends SiteController { const router = express.Router(); router.use(async (req, res, next) => { res.locals.currentView = 'admin'; - res.locals.adminView = 'venue'; + res.locals.adminView = 'site-link'; return next(); }); diff --git a/app/controllers/author.js b/app/controllers/author.js index ad5bd5b..cb0e79f 100644 --- a/app/controllers/author.js +++ b/app/controllers/author.js @@ -20,33 +20,32 @@ class AuthorController extends SiteController { const { limiter: limiterService } = dtp.services; const { author: authorLimiter } = limiterService.config; - // const upload = multer({ dest: `/tmp/dtp-sites/${this.dtp.config.site.domainKey}`}); - async function checkPermissions(req, res) { + async function checkPermissions(req, res, next) { if (!req.user){ - return false + return res.redirect(302, '/'); } const canAuthor = req.user.permissions.canAuthorPages || req.user.permissions.canAuthorPosts; const canPublish = req.user.permissions.canPublishPages || req.user.permissions.canPublishPosts; if (!canAuthor && !canPublish) { - return false; + return next(new SiteError(403, 'Author privileges are required')); } - return true; + return next(); + } + + async function checkAuthorPublisherPermissions (req, res, next) { + res.locals.currentView = 'author'; + const canAuthorOrPublish = await checkPermissions(req, res, next); + return canAuthorOrPublish; } + // const upload = multer({ dest: `/tmp/dtp-sites/${this.dtp.config.site.domainKey}`}); const router = express.Router(); dtp.app.use('/author', router); + router.use(checkAuthorPublisherPermissions); - router.use(async (req, res, next) => { - res.locals.currentView = 'author'; - const canAuthorOrPublish = await checkPermissions(req, res); - if (!canAuthorOrPublish) { - return next(new SiteError(403, 'Author privileges are required')); - } - return next(); - }); router.get('/post', limiterService.createMiddleware(authorLimiter.getPostIndex), @@ -79,12 +78,30 @@ class AuthorController extends SiteController { async getAuthorHome (req, res, next) { const { /*comment: commentService,*/ post: postService } = this.dtp.services; try { - res.locals.drafts = await postService.getForAuthor(req.user, ['draft'], { skip: 0, cpp: 5 }); - res.locals.published = await postService.getForAuthor(req.user, ['published'], { skip: 0, cpp: 5 }); - - res.locals.pagination = this.getPaginationParameters(req, 20); - res.locals.authorComments = await postService.getCommentsForAuthor(req.user, res.locals.pagination); - + const canAuthor = req.user.permissions.canAuthorPosts; + const canPublish = req.user.permissions.canPublishPosts; + if(canAuthor){ + + if(canPublish){ + + res.locals.published = await postService.getPosts({ skip: 0, cpp: 5 }); + res.locals.drafts = await postService.getPosts({ skip: 0, cpp: 5 }, ['draft']); + + res.locals.authorComments = await postService.getCommentsForAuthor(req.user, res.locals.pagination); + res.locals.pagination = this.getPaginationParameters(req, 20); + } else { + + res.locals.drafts = await postService.getForAuthor(req.user, ['draft'], { skip: 0, cpp: 5 }); + res.locals.published = await postService.getForAuthor(req.user, ['published'], { skip: 0, cpp: 5 }); + + res.locals.pagination = this.getPaginationParameters(req, 20); + res.locals.authorComments = await postService.getCommentsForAuthor(req.user, res.locals.pagination); + } + // return res.render('author/index'); + } + else if (canPublish){ + res.locals.posts = await postService.getAllPosts({ skip: 0, cpp: 5 }); + } res.render('author/index'); } catch (error) { this.log.error('failed to render Author dashboard', { error }); diff --git a/app/controllers/post.js b/app/controllers/post.js index f020de7..d0f17c3 100644 --- a/app/controllers/post.js +++ b/app/controllers/post.js @@ -190,7 +190,8 @@ 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)) { + 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); @@ -317,7 +318,8 @@ 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)) { + if (!req.user._id.equals(res.locals.post.author._id) || + !req.user.permissions.canPublishPosts) { throw new SiteError(403, 'This is not your post'); } diff --git a/app/views/author/index.pug b/app/views/author/index.pug index 23c10f9..c910b1f 100644 --- a/app/views/author/index.pug +++ b/app/views/author/index.pug @@ -9,42 +9,95 @@ block content include ../comment/components/comment - section.uk-section.uk-section-default.uk-section-xsmall - .uk-container.uk-container-expand - div(uk-grid).uk-flex-middle - .uk-width-expand - h1.uk-margin-remove Author Dashboard - .uk-width-auto - a(href= "/post/compose").uk-button.uk-button-primary.uk-border-rounded - span - i.fas.fa-plus - span.uk-margin-small-left.uk-text-bold Create Post - - div(uk-grid) - if authorComments.comments.length > 0 - div(class="uk-width-1-1 uk-width-2-3@m") - +renderSectionTitle('Recent Comments', { url: '/author/comments', title: 'See All', label: 'SEE ALL' }) - .content-block - .uk-margin - ul#post-comment-list.uk-list.uk-list-divider.uk-list-large.uk-margin - each comment in authorComments.comments - li - .uk-margin - a(href=`/post/${comment.resource.slug}`).uk-display-block.uk-link-reset - +renderPostSummary(comment.resource) - +renderComment(comment, { }) - - +renderPaginationBar('/author', published.totalPostCount) - else - div(class="uk-width-1-1 uk-width-2-3@m") - +renderSectionTitle('Comments') - div There are no comments. - - div(class="uk-width-1-1 uk-width-1-3@m") - .uk-margin - +renderSectionTitle('Drafts') - +renderPostDraftList(drafts.posts) - - .uk-margin - +renderSectionTitle('Recent Posts', { title: 'View All', label: 'View All', url: '/author/post' }) - +renderPostList(published.posts) \ No newline at end of file + if (user.permissions.canAuthorPosts && user.permissions.canPublishPosts) + section.uk-section.uk-section-default.uk-section-xsmall + .uk-container.uk-container-expand + div(uk-grid).uk-flex-middle + .uk-width-expand + h1.uk-margin-remove Author Dashboard + .uk-width-auto + a(href= "/post/compose").uk-button.uk-button-primary.uk-border-rounded + span + i.fas.fa-plus + span.uk-margin-small-left.uk-text-bold Create Post + div(uk-grid) + if authorComments.length > 0 + div(class="uk-width-1-1 uk-width-2-3@m") + +renderSectionTitle('Recent Comments', { url: '/author/comments', title: 'See All', label: 'SEE ALL' }) + .content-block + .uk-margin + ul#post-comment-list.uk-list.uk-list-divider.uk-list-large.uk-margin + each comment in authorComments + li + .uk-margin + a(href=`/post/${comment.resource.slug}`).uk-display-block.uk-link-reset + +renderPostSummary(comment.resource) + +renderComment(comment, { }) + + +renderPaginationBar('/author', published.totalPostCount) + else + div(class="uk-width-1-1 uk-width-2-3@m") + +renderSectionTitle('Comments') + div There are no comments. + + div(class="uk-width-1-1 uk-width-1-3@m") + .uk-margin + +renderSectionTitle('Drafts') + +renderPostDraftList(drafts) + + .uk-margin + +renderSectionTitle('Recent Posts', { title: 'View All', label: 'View All', url: '/author/post' }) + +renderPostList(published) + + else if user.permissions.canAuthorPosts + section.uk-section.uk-section-default.uk-section-xsmall + .uk-container.uk-container-expand + div(uk-grid).uk-flex-middle + .uk-width-expand + h1.uk-margin-remove Author Dashboard + .uk-width-auto + a(href= "/post/compose").uk-button.uk-button-primary.uk-border-rounded + span + i.fas.fa-plus + span.uk-margin-small-left.uk-text-bold Create Post + div(uk-grid) + if authorComments.comments.length > 0 + div(class="uk-width-1-1 uk-width-2-3@m") + +renderSectionTitle('Recent Comments', { url: '/author/comments', title: 'See All', label: 'SEE ALL' }) + .content-block + .uk-margin + ul#post-comment-list.uk-list.uk-list-divider.uk-list-large.uk-margin + each comment in authorComments.comments + li + .uk-margin + a(href=`/post/${comment.resource.slug}`).uk-display-block.uk-link-reset + +renderPostSummary(comment.resource) + +renderComment(comment, { }) + + +renderPaginationBar('/author', published.totalPostCount) + else + div(class="uk-width-1-1 uk-width-2-3@m") + +renderSectionTitle('Comments') + div There are no comments. + + div(class="uk-width-1-1 uk-width-1-3@m") + .uk-margin + +renderSectionTitle('Drafts') + +renderPostDraftList(drafts.posts) + + .uk-margin + +renderSectionTitle('Recent Posts', { title: 'View All', label: 'View All', url: '/author/post' }) + +renderPostList(published.posts) + + else if user.permissions.canPublishPosts + section.uk-section.uk-section-default.uk-section-xsmall + .uk-container.uk-container-expand + div(uk-grid).uk-flex-middle + .uk-width-expand + h1.uk-margin-remove Author Dashboard + .uk-width-auto + .uk-margin + div(class="uk-width-1-1 uk-width-3-3@m") + .uk-margin + +renderSectionTitle('All Posts') + +renderPostList(posts) \ No newline at end of file diff --git a/app/views/otp/welcome.pug b/app/views/otp/welcome.pug index 2d4bb84..c03ac7d 100644 --- a/app/views/otp/welcome.pug +++ b/app/views/otp/welcome.pug @@ -41,7 +41,7 @@ block content .uk-margin button(type="submit").uk-button.dtp-button-primary.uk-border-pill Enable 2FA - div(class="uk-width-1-1 uk-text-center uk-text-left@m", hidden) + div(class="uk-width-1-1 uk-text-center uk-text-left@m") .uk-margin p Or, if your authenticator doesn't support scanning QR codes, you can enter the OTP configuration information shown here to begin displaying codes: pre( diff --git a/app/views/post/view.pug b/app/views/post/view.pug index 51623c1..1994378 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) + if post.author._id.equals(user._id) || user.permissions.canPublishPosts .uk-width-auto a(href=`/post/${post._id}/edit`).uk-display-block +renderButtonIcon('fa-pen', 'edit') diff --git a/app/views/welcome/signup.pug b/app/views/welcome/signup.pug index 6e0ccc2..7d8fb05 100644 --- a/app/views/welcome/signup.pug +++ b/app/views/welcome/signup.pug @@ -14,7 +14,7 @@ block content .uk-margin label(for="email").uk-form-label Email input(id="email", name="email", type="email", autocomplete="off", placeholder="Enter your email address").uk-input - .uk-text-small.uk-text-muted.uk-margin-small-top(class="uk-visible@m") I'm throwing your email address away after the demo, and I'm not verifying it. You won't receive email. + .uk-text-small.uk-text-muted.uk-margin-small-top(class="uk-visible@m") Enter a valid email, and not one from a temporary service .uk-margin label(for="username").uk-form-label Username