diff --git a/content b/content index 50554ca..0005a42 160000 --- a/content +++ b/content @@ -1 +1 @@ -Subproject commit 50554ca98931f8e1e971b6cd3e055ca90f315fea +Subproject commit 0005a42f8d5d8487d1f45c37d9823a6fd8936231 diff --git a/src/controllers/blogControllers.js b/src/controllers/blogControllers.js index 86558bd..b4f21ff 100644 --- a/src/controllers/blogControllers.js +++ b/src/controllers/blogControllers.js @@ -71,6 +71,7 @@ } const htmlContent = marked(content); const context = { + showSidebar: !res.locals.isPaper, title: frontmatter.title, date: frontmatter.date, author: frontmatter.author, @@ -121,5 +122,8 @@ templateContent: post.excerpt || "", })); - res.renderWithBaseContext("pages/blog_index", { collections: { posts } }); + res.renderWithBaseContext("pages/blog_index", { + showSidebar: !res.locals.isPaper, + collections: { posts }, + }); }; diff --git a/src/middleware/baseContext.js b/src/middleware/baseContext.js index 7cf3640..e0570e6 100644 --- a/src/middleware/baseContext.js +++ b/src/middleware/baseContext.js @@ -24,10 +24,10 @@ function cssOverride(overrides = {}) { const defaults = { classes: { - body: "pattern-dots", - layout: "layout", - sidebar: "sidebar", - container: "container", + body: "pattern-dots no-print", + layout: "layout no-print", + sidebar: "sidebar no-print", + container: "container no-print", }, styles: {}, }; @@ -41,7 +41,7 @@ const getDefaultContext = (view = "web") => { const isPaper = view == "paper"; return { - showSidebar: !isPaper, + showSidebar: false, showFooter: !isPaper, showHeader: !isPaper, viewType: view, @@ -50,6 +50,7 @@ }; module.exports.attachBaseContextGetter = async (req, res, next) => { + res.locals.isPaper = req.query.view == "print"; req.getBaseContext = async (isAuthenticated, overrides = {}) => { const filteredNavLinks = processMenuLinks( navLinks, diff --git a/src/middleware/index.js b/src/middleware/index.js index e0684d9..4de2b80 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -9,6 +9,7 @@ const validateRequestIntegrity = require("./validateRequestIntegrity"); const errorHandler = require("./errorHandler"); const { attachBaseContextGetter, buildBaseContext } = require("./baseContext"); +const BaseContext = require("../utils/baseContext"); const hbs = require("./hbs"); const authCheck = require("./authCheck"); const { redirectMiddleware } = require("./redirect"); @@ -40,7 +41,8 @@ app.use(authCheck); // Setup handlebars - app.use(attachBaseContextGetter, buildBaseContext); + app.use(BaseContext); + // app.use(attachBaseContextGetter, buildBaseContext); // Setup production environment if (meta.node_env === "production" || meta.node_env === "testing") { diff --git a/src/routes/projects.js b/src/routes/projects.js index 5137b81..c7220c6 100644 --- a/src/routes/projects.js +++ b/src/routes/projects.js @@ -22,15 +22,17 @@ router.use("/projects/website-presentation", presentation); html.register("/games/word-guesser", "word-guesser"); -function createProject(url, file) { - markdown.register(url, file, { project: true }); +function createProject(url, file, overrides = {}) { + markdown.register(url, file, { project: true, ...overrides }); } createProject("/projects/lisp-interpreter", "projects/lisp_interpreter"); createProject("/projects/pipeline-runner", "projects/pipeline_runner"); createProject("/projects/telemetry", "projects/telemetry"); createProject("/projects/xmonad", "projects/xmonad"); createProject("/projects/word-guesser", "projects/word-guesser"); -createProject("/about/blog", "projects/about-blog"); +createProject("/about/blog", "projects/about-blog", { + // showSidebar: true, +}); router.get("/projects", async (req, res, next) => { try { diff --git a/src/utils/MarkdownRoutes.js b/src/utils/MarkdownRoutes.js index 20fec17..56c11b6 100644 --- a/src/utils/MarkdownRoutes.js +++ b/src/utils/MarkdownRoutes.js @@ -62,6 +62,7 @@ content: htmlContent, ...extraParams, }; + console.log(extraParams); res.renderWithBaseContext(`pages/${handlebarsFile}`, context); } catch (err) { err.statusCode = 500; diff --git a/src/utils/baseContext.js b/src/utils/baseContext.js new file mode 100644 index 0000000..02367e5 --- /dev/null +++ b/src/utils/baseContext.js @@ -0,0 +1,123 @@ +// src/utils/baseContext.js +const path = require("path"); +const getPostsMenu = require("../services/postsMenuService"); +const { formatMonth } = require("../utils/formatMonth"); +const { qualifyNavLinks, qualifyLink } = require("../utils/qualifyLinks"); +const { baseUrl } = require("../utils/baseUrl.js"); +const navLinks = require(path.join(__dirname, "../../content/navLinks.json")); +const processMenuLinks = require("../utils/processMenuLinks"); +const { generateToken } = require("../utils/adminToken"); +const { meta } = require("../config/loader"); + +const getSiteTitle = (owner) => `${owner}'s Software Blog`; + +const POSTS_DIR = path.join(__dirname, "../../content/posts"); + +class BaseContextManager { + constructor(req, res, next) { + this.req = req; + this.res = res; + this.next = next; + this.isPaper = req.query.view == "print"; + req.getBaseContext = this.getBaseContext.bind(this); + res.renderWithBaseContext = this.renderWithBaseContext.bind(this); + res.renderWithCallback = this.renderWithCallback.bind(this); + res.renderGenericMessage = this.renderGenericMessage.bind(this); + res.cssOverride = this.cssOverride.bind(this); + } + + async init() { + const isAuthenticated = this.req.isAuthenticated; + const token = generateToken(); + const adminLoginUrl = qualifyLink(`/${token}`); + this.baseContext = await this.getBaseContext(isAuthenticated, { + adminLoginUrl, + }); + this.next(); + } + + /** + * Merges CSS class and style overrides with default values. + * @param {Object} overrides - Object containing classes and styles to override. + * @returns {Object} The merged CSS configuration object. + */ + cssOverride(overrides = {}) { + const defaults = { + classes: { + body: "pattern-dots no-print", + layout: "layout no-print", + sidebar: "sidebar no-print", + container: "container no-print", + }, + styles: {}, + }; + + return { + classes: { ...defaults.classes, ...(overrides.classes || {}) }, + styles: { ...defaults.styles, ...(overrides.styles || {}) }, + }; + } + + getDefaultContext(view = "web") { + const isPaper = view == "paper"; + return { + showSidebar: false, + showFooter: !isPaper, + showHeader: !isPaper, + viewType: view, + css: this.cssOverride(), + }; + } + + async getBaseContext(isAuthenticated, overrides = {}) { + const filteredNavLinks = processMenuLinks( + navLinks, + isAuthenticated, + this.req.path, + ); + const qualifiedNavLinks = qualifyNavLinks(filteredNavLinks); + const menu = await getPostsMenu(POSTS_DIR); + const siteOwner = meta.site_owner; + + const context = { + title: getSiteTitle(siteOwner), + siteOwner, + originCountry: meta.country, + hCaptchaKey: meta.hcaptcha_key, + navLinks: qualifiedNavLinks, + years: menu, + formatMonth, + baseUrl, + isAuthenticated, + node_env_dev: meta.node_env == "development", + node_env_prod: meta.node_env != "development", + ...this.getDefaultContext(this.req.query.view ?? "web"), + ...overrides, + }; + + return context; + } + + renderWithBaseContext(template, overrides = {}) { + const context = Object.assign({}, this.baseContext, overrides); + this.res.render(template, context); + } + + renderWithCallback(template, cb, overrides = {}) { + let context = Object.assign({}, this.baseContext, overrides); + this.res.logger.info(cb); // Retained mission critical log + context = cb(context); + this.res.render(template, context); + } + + renderGenericMessage(overrides = {}) { + this.res.render( + "pages/generic-message", + Object.assign({}, this.baseContext, overrides), + ); + } +} + +module.exports = async (req, res, next) => { + await new BaseContextManager(req, res, next).init(); +};