// page.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 Page = mongoose.model('Page'); class PageService extends SiteService { constructor (dtp) { super(dtp, module.exports); } async menuMiddleware (req, res, next) { try { const pages = await Page.find({ parent: { $exists: false } }).lean(); res.locals.mainMenu = pages .map((page) => { return { url: `/page/${page.slug}`, label: page.menu.label, order: page.menu.order, }; }) .sort((a, b) => { return a.order < b.order; }); return next(); } catch (error) { this.log.error('failed to build page menu', { error }); return next(); } } async create (author, pageDefinition) { const page = new Page(); page.title = striptags(pageDefinition.title.trim()); page.slug = this.createPageSlug(page._id, page.title); page.content = pageDefinition.content.trim(); page.status = pageDefinition.status || 'draft'; page.menu = { label: pageDefinition.menuLabel || page.title.slice(0, 10), order: parseInt(pageDefinition.menuOrder || '0', 10), }; if (pageDefinition.parentPageId && (pageDefinition.parentPageId !== 'none')) { page.menu.parent = pageDefinition.parentPageId; } await page.save(); return page.toObject(); } async update (page, pageDefinition) { const NOW = new Date(); const updateOp = { $set: { updated: NOW, }, }; if (pageDefinition.title) { updateOp.$set.title = striptags(pageDefinition.title.trim()); } if (pageDefinition.slug) { let pageSlug = striptags(slug(pageDefinition.slug.trim())).split('-'); while (ObjectId.isValid(pageSlug[pageSlug.length - 1])) { pageSlug.pop(); } pageSlug = pageSlug.splice(0, 4); pageSlug.push(page._id.toString()); updateOp.$set.slug = `${pageSlug.join('-')}`; } if (pageDefinition.summary) { updateOp.$set.summary = striptags(pageDefinition.summary.trim()); } if (pageDefinition.content) { updateOp.$set.content = pageDefinition.content.trim(); } if (pageDefinition.status) { updateOp.$set.status = striptags(pageDefinition.status.trim()); } updateOp.$set.menu = { label: pageDefinition.menuLabel || updateOp.$set.title.slice(0, 10), order: parseInt(pageDefinition.menuOrder || '0', 10), }; if (pageDefinition.parentPageId && (pageDefinition.parentPageId !== 'none')) { updateOp.$set.menu.parent = pageDefinition.parentPageId; } await Page.updateOne( { _id: page._id }, updateOp, { upsert: true }, ); } async getPages (pagination, status = ['published']) { if (!Array.isArray(status)) { status = [status]; } const pages = await Page .find({ status: { $in: status } }) .sort({ created: -1 }) .skip(pagination.skip) .limit(pagination.cpp) .lean(); return pages; } async getById (pageId) { const page = await Page .findById(pageId) .select('+content') .lean(); return page; } async getBySlug (pageSlug) { const slugParts = pageSlug.split('-'); const pageId = slugParts[slugParts.length - 1]; return this.getById(pageId); } async getAvailablePages (excludedPageIds) { const search = { }; if (excludedPageIds) { search._id = { $nin: excludedPageIds }; } const pages = await Page.find(search).lean(); return pages; } async deletePage (page) { this.log.info('deleting page', { pageId: page._id }); await Page.deleteOne({ _id: page._id }); } createPageSlug (pageId, pageTitle) { if ((typeof pageTitle !== 'string') || (pageTitle.length < 1)) { throw new Error('Invalid input for making a page slug'); } const pageSlug = slug(pageTitle.trim().toLowerCase()).split('-').slice(0, 4).join('-'); return `${pageSlug}-${pageId}`; } } module.exports = { slug: 'page', name: 'page', create: (dtp) => { return new PageService(dtp); }, };