// post.js // Copyright (C) 2021 Digital Telepresence, LLC // License: Apache-2.0 'use strict'; const striptags = require('striptags'); const slug = require('slug'); const { SiteService } = require('../../lib/site-lib'); const mongoose = require('mongoose'); const ObjectId = mongoose.Types.ObjectId; const Post = mongoose.model('Post'); class PostService extends SiteService { constructor (dtp) { super(dtp, module.exports); this.populatePost = [ { path: 'author', select: '_id username username_lc displayName picture', }, ]; } async create (author, postDefinition) { const NOW = new Date(); const post = new Post(); post.created = NOW; post.author = author._id; post.title = striptags(postDefinition.title.trim()); post.slug = this.createPostSlug(post._id, post.title); post.summary = striptags(postDefinition.summary.trim()); post.content = postDefinition.content.trim(); post.status = postDefinition.status || 'draft'; post.flags = { enableComments: postDefinition.enableComments === 'on', isFeatured: postDefinition.isFeatured === 'on', }; await post.save(); return post.toObject(); } async update (post, postDefinition) { const NOW = new Date(); const updateOp = { $set: { updated: NOW, }, }; if (postDefinition.title) { updateOp.$set.title = striptags(postDefinition.title.trim()); updateOp.$set.slug = this.createPostSlug(post._id, updateOp.$set.title); } if (postDefinition.slug) { let postSlug = striptags(slug(postDefinition.slug.trim())).split('-'); while (ObjectId.isValid(postSlug[postSlug.length - 1])) { postSlug.pop(); } postSlug = postSlug.splice(0, 4); postSlug.push(post._id.toString()); updateOp.$set.slug = `${postSlug.join('-')}`; } if (postDefinition.summary) { updateOp.$set.summary = striptags(postDefinition.summary.trim()); } if (postDefinition.content) { updateOp.$set.content = postDefinition.content.trim(); } if (postDefinition.status) { updateOp.$set.status = striptags(postDefinition.status.trim()); } updateOp.$set['flags.enableComments'] = postDefinition.enableComments === 'on'; updateOp.$set['flags.isFeatured'] = postDefinition.isFeatured === 'on'; await Post.updateOne( { _id: post._id }, updateOp, { upsert: true }, ); } async getPosts (pagination, status = ['published']) { if (!Array.isArray(status)) { status = [status]; } const posts = await Post .find({ status: { $in: status } }) .sort({ created: -1 }) .skip(pagination.skip) .limit(pagination.cpp) .lean(); return posts; } async getById (postId) { const post = await Post .findById(postId) .select('+content') .populate(this.populatePost) .lean(); return post; } async getBySlug (postSlug) { const slugParts = postSlug.split('-'); const postId = slugParts[slugParts.length - 1]; return this.getById(postId); } async deletePost (post) { this.log.info('deleting post', { postId: post._id }); await Post.deleteOne({ _id: post._id }); } createPostSlug (postId, postTitle) { if ((typeof postTitle !== 'string') || (postTitle.length < 1)) { throw new Error('Invalid input for making a post slug'); } const postSlug = slug(postTitle.trim().toLowerCase()).split('-').slice(0, 4).join('-'); return `${postSlug}-${postId}`; } } module.exports = { slug: 'post', name: 'post', create: (dtp) => { return new PostService(dtp); }, };