const { Controller } = require('libflitter') const ncp = require('ncp').ncp const fs = require('fs').promises const path = require('path') class ExportController extends Controller { static get services() { return [...super.services, 'models', 'utility'] } async get_export_list(req, res, next) { const Export = this.models.get('api:Export') const exports = await Export.find({ user_id: req.user.id, }) return res.api(exports) } async export_subtree(req, res, next) { const format = req.form.format const page = req.form.page if ( format === 'html' ) { const generated_export = await this.export_subtree_as_html(page, req.user) } } async export_subtree_as_html(page, user) { const flat_tree = [] const add_to_tree = async (page, level = 0) => { if ( await page.is_accessible_by(user, 'view') ) { flat_tree.push({ level, page, file_name: `${page.Name.replace(/\s/g, '-')}-${page.UUID}.html`, }) const children = await page.childPages for ( const child of children ) { await add_to_tree(child, level + 1) } } } await add_to_tree(page) const manifest = { sidebar: [], } for ( const item of flat_tree ) { manifest.sidebar.push({ title: item.page.Name, level: item.level, link: item.file_name, }) } // Copy the template over const work_dir = await this.scratch_dir() await this.copy_template(this.utility.path('app', 'assets', 'export', 'html'), work_dir) const html_template = await fs.readFile(path.resolve(work_dir, 'index.html'), 'utf-8') for ( const item of flat_tree ) { let item_template = html_template item_template = item_template.replace(/{{\s?MANIFEST\s?}}/g, JSON.stringify(manifest)) item_template = item_template.replace(/{{\s?GROUP_TITLE\s?}}/g, page.Name) item_template = item_template.replace(/{{\s?PAGE_TITLE\s?}}/g, item.page.Name) item_template = item_template.replace(/{{\s?PAGE_CONTENT\s?}}/g, await this.page_as_html(item.page, work_dir)) await fs.writeFile(path.resolve(work_dir, item.file_name), item_template) } return work_dir } async copy_template(from, to) { return new Promise((res, rej) => { ncp(from, to, err => { if ( err ) rej(err) else res() }) }) } async scratch_dir() { const tmp = require('tmp') return new Promise((res, rej) => { tmp.dir((err, path) => { if ( err ) rej(err) else res(path) }) }) } async page_as_html(page, work_dir) { const Codium = this.models.get('api:Codium') const FileGroup = this.models.get('api:FileGroup') const File = this.models.get('upload::File') let html = '' const nodes = await page.nodes for ( const node of nodes ) { // ATM, there are 4 node types: norm, database_ref, files_ref, and code_ref if ( node.Value.Mode === 'norm' ) { html += node.Value.Value } else if ( node.Type === 'code_ref' ) { const code = await Codium.findOne({ UUID: node.Value.Value }) if ( code ) { const snip_file = `code-snippet-${code.UUID}.txt` await fs.writeFile(path.resolve(work_dir, snip_file), code.code) html += `
` } } else if ( node.Type === 'file_ref' ) { const file_group = await FileGroup.findOne({ UUID: node.Value.Value }) if ( file_group ) { const file_htmls = [] for ( const file_id of file_group.FileIds ) { const file = await File.findById(file_id) if ( file ) { const store_path = file.provider().filepath(file.store_id) await this.copy_template(store_path, path.resolve(work_dir, `file-${file.upload_name}`)) file_htmls.push(`
${file.original_name}
`) } } html += `
${file_htmls.join('\n')}
` } } } return html } } module.exports = exports = ExportController