import * as DataEvent from '../../src/com/events/DataEvent'; import StringUtils from '../../src/com/utils/StringUtils'; import Settings, { SETTINGS_FILE, SETTINGS_TAG } from './Settings'; import fs from 'fs-extra'; import sanitize from 'sanitize-html'; import Utils from './Utils'; const pug = require('pug'); const md = require('markdown-it')('commonmark'); const _ = require('lodash'); const moment = require('moment'); const settings = new Settings(); export default class Render { //-------------------------- // constructor //-------------------------- constructor() {} //-------------------------- // methods //-------------------------- start() {} /** * Renders all pages from markdown to html * @parameter pages: payload for site pages * @parameter theme: current theme being used as defined in settings */ publishAll(pages, theme, author) { return new Promise((resolve, reject) => { settings .load(SETTINGS_FILE) .then(config => { let response = []; let count = _.filter(pages, page => { return page.metadata.deleted === false && page.metadata.published === true; }).length; let rendered = 0; let display_count = 0; let recent = []; let featured = _.filter(pages, page => { return ( page.metadata.deleted === false && page.metadata.published === true && page.metadata.featured === true ); }); for (let index = 0; index < pages.length; index++) { pages.sort((a, b) => parseFloat(b.metadata.id) - parseFloat(a.metadata.id)); const page = pages[index]; if (page.metadata.deleted === false && page.metadata.published === true) { if (page.metadata.layout != 'index') { if (recent.length < config.global.display_limit) { recent.push({ title: page.metadata.title, slug: page.metadata.slug, feature: page.metadata.feature, created: moment(page.metadata.created).fromNow(), path: page.metadata.path }); display_count = ++display_count; } } let writeFile, template; let path = 'public/' + moment(page.metadata.created).format('YYYY') + '/' + moment(page.metadata.created).format('MM') + '/'; if (page.metadata.layout === 'index') { template = 'content/themes/' + theme + '/index.pug'; writeFile = 'public/index.html'; } else { writeFile = path + page.metadata.slug + '.html'; template = 'content/themes/' + theme + '/page.pug'; } let buffed = sanitize(page.content, { allowedTags: ['del', 'a', 'iframe', 'img'], allowedAttributes: { a: ['href', 'name', 'target'], img: ['src'], iframe: [ 'height', 'width', 'src', 'frameborder', 'allow', 'allowfullscreen' ] } }); let bag = page.metadata.tags.split(','); let tags = []; for (let index = 0; index < bag.length; index++) { let tag = bag[index].trim(); tags.push({ label: bag[index], slug: new StringUtils().cleanString(tag) }); } buffed = new StringUtils().decodeHTML(buffed); let html = md.render(buffed, { html: true, xhtmlOut: true }); let file = pug.renderFile(template, { title: page.metadata.title, default_bg: page.metadata.feature, content: html, tags: tags, menu: config.menu, recent_posts: recent, featured_posts: featured, meta: { who: author, when: moment(page.metadata.created).fromNow(), tags: tags }, welcome_message: page.metadata.title }); fs.ensureDir(path).then(() => { fs.writeFile(writeFile, file, err => { // throws an error, you could also catch it here if (err) { response = { type: DataEvent.PAGES_NOT_RENDERED, message: err }; reject(response); } // success case, the file was saved }); }); rendered = ++rendered; if (rendered === count) { response = { type: DataEvent.PAGES_RENDERED, message: 'All Pages Rendered. Sweet.' }; //move theme assets to public when pages are rendered new Utils().moveAssets(); resolve(response); } } else { if (count === 0) { response = { type: DataEvent.PAGES_RENDERED, message: 'No page rendering needed' }; resolve(response); } //check to see if deleted pages have been renderered and delete them if (page.metadata.layout !== 'index') { fs.unlink( 'public/' + page.metadata.path + '/' + page.metadata.slug + '.html' ) .then() .catch(() => { //console.log('ERROR', err); }); } } } }) .catch(err => { //console.log('ERROR', err); reject(err); }); }); } /** * Method to extract, group and render tags in page * @parameter pages: payload for site pages */ publishTags(pages) { let self = this; return new Promise((resolve, reject) => { self.loadRenderData() .then(result => { let tags = result.tags.tags; let renderList = []; for (let index = 0; index < tags.length; index++) { let tag = tags[index]; //console.log('**TAG**', tag.tag_name); var pageList = []; for (let i = 0; i < pages.length; i++) { let page = pages[i]; //TODO: filter for deleted and unpublished pages if ( page.metadata.deleted === false && page.metadata.published === true ) { if (_.includes(page.metadata.tags, tag.tag_name)) { pageList.push({ title: page.metadata.title, slug: page.metadata.slug }); } } } renderList.push({ tag: tag.tag_name, tag_list: pageList, slug: tag.slug }); } let response = []; for (let index = 0; index < renderList.length; index++) { let item = renderList[index]; let file = pug.renderFile( 'content/themes/' + result.settings.global.theme + '/tags.pug', { title: item.tag, default_bg: result.settings.global.background, content_tags: 'THESE ARE TAGS', tag_list: item.tag_list, menu: result.settings.menu } ); fs.ensureDir('public/tags', () => { fs.writeFile('public/tags/' + item.slug + '.html', file, err => { // throws an error, you could also catch it here if (err) { response = { type: DataEvent.TAG_PAGES_NOT_RENDERED, message: err }; reject(response); } // success case, the file was saved response = { type: DataEvent.TAG_PAGES_RENDERED, message: 'Tag Pages ready to go. Good job.' }; resolve(response); }); }); } }) .catch(err => { reject(err); }); }); } /** * Method to build page that lists all active pages, organized by year and month * @parameter pages: payload for site pages */ publishArchive(archive) { settings .load(SETTINGS_FILE) .then(settings => { let file = pug.renderFile( 'content/themes/' + settings.global.theme + '/archive.pug', { title: 'ARCHIVES', default_bg: settings.global.background, content_tags: 'COLD STORAGE', archives: archive, menu: settings.menu } ); fs.writeFile('public/archives.html', file, err => { // throws an error, you could also catch it here if (err) { //console.log('ERROR', err); //response = { type: DataEvent.TAG_PAGES_NOT_RENDERED, message: err }; } // success case, the file was saved }); }) .catch(() => { //console.log(err); }); } loadRenderData() { return new Promise((resolve, reject) => { let getSettings = settings.load(SETTINGS_FILE); let getTags = settings.load(SETTINGS_TAG); Promise.all([getSettings, getTags]) .then(result => { const [settings, tags] = result; let data = { settings: settings, tags: tags }; resolve(data); }) .catch(err => { reject(err); }); }); } //-------------------------- // event handlers //-------------------------- }