X-Git-Url: https://git.squeep.com/?a=blobdiff_plain;f=src%2Ftemplate%2Ftemplate-helper.js;h=2da05dfb928007fa1f080bb35c7ab4770262d9f2;hb=b806715f9288323cce7b0ab437ee78b01d26c548;hp=d3962f99f263a07c485501236d0b1b3c3797848d;hpb=264038940f8e870d831ea9003bf40467476a73b6;p=websub-hub diff --git a/src/template/template-helper.js b/src/template/template-helper.js index d3962f9..2da05df 100644 --- a/src/template/template-helper.js +++ b/src/template/template-helper.js @@ -1,80 +1,35 @@ 'use strict'; -/** - * A bunch of shorthand to put together common parts of an HTML page. - */ - -/** - * Some fields may have values outside normal dates, handle them here. - * @param {Date} date - * @param {String} otherwise - */ -const dateOrNot = (date, otherwise) => { - if (!date) { - return otherwise; - } - if (typeof date === 'number') { - date = new Date(date); - } - const dateMs = date.getTime(); - if (!Number.isFinite(dateMs) - || dateMs == 0) { - return otherwise; - } - return date.toString(); -}; - - -/** - * Render a duration. - * @param {Number} seconds - * @returns {String} - */ -const secondsToPeriod = (seconds) => { - let value = seconds; - const result = []; - - const nextResult = (factor, label) => { - const r = factor ? value % factor : value; - if (r) { - result.push(`${r} ${label}${r != 1 ? 's' : ''}`); - } - value = factor ? Math.floor(value / factor) : value; - } - - nextResult(60, 'second'); - nextResult(60, 'minute'); - nextResult(24, 'hour'); - nextResult(30, 'day'); - nextResult(undefined, 'month'); - - result.reverse(); - return result.join(' '); -}; - +const { TemplateHelper } = require('@squeep/html-template-helper'); +const { Message } = require('../enum'); /** * Render a topic as a row of details. - * @param {Object} topic - * @param {Object[]} subscribers - * @param {Boolean} detailsLink - * @returns {String} + * @param {object} topic topic + * @param {object[]} subscribers subscribers + * @param {boolean} detailsLink link to details + * @returns {string} html */ function renderTopicRow(topic, subscribers, detailsLink = true) { + if (!topic) { + return ` + (topic not found) +`; + } return ` ${detailsLink ? '' : ''}${topic.url}${detailsLink ? '' : ''} ${subscribers.length} - ${dateOrNot(topic.created, 'Unknown')} - ${secondsToPeriod(topic.leaseSecondsPreferred)} - ${secondsToPeriod(topic.leaseSecondsMin)} - ${secondsToPeriod(topic.leaseSecondsMax)} + ${TemplateHelper.dateFormat(topic.created, Message.EndOfTime, Message.BeginningOfTime, Message.Unknown)} + ${TemplateHelper.secondsToPeriod(topic.leaseSecondsPreferred)} + ${TemplateHelper.secondsToPeriod(topic.leaseSecondsMin)} + ${TemplateHelper.secondsToPeriod(topic.leaseSecondsMax)} ${topic.publisherValidationUrl ? topic.publisherValidationUrl : 'None'} ${topic.isActive} ${topic.isDeleted} - ${dateOrNot(topic.lastPublish, 'Never')} - ${dateOrNot(topic.contentFetchNextAttempt, 'Next Publish')} + ${TemplateHelper.dateFormat(topic.lastPublish, Message.EndOfTime, Message.Never, Message.Never)} + ${TemplateHelper.dateFormat(topic.contentFetchNextAttempt, Message.NextPublish, Message.Pending, Message.NextPublish)} ${topic.contentFetchAttemptsSinceSuccess} - ${dateOrNot(topic.contentUpdated, 'Never')} + ${TemplateHelper.dateFormat(topic.contentUpdated, Message.EndOfTime, Message.Never, Message.Never)} ${topic.contentType} ${topic.id} `; @@ -83,7 +38,7 @@ function renderTopicRow(topic, subscribers, detailsLink = true) { /** * Render the header row for topic details. - * @returns {String} + * @returns {string} html */ function renderTopicRowHeader() { return ` @@ -101,29 +56,34 @@ function renderTopicRowHeader() { Content Fetch Failures Content Updated Content Type - ID + Internal Id `; } /** * Render a subscription as a row of details. - * @param {Object} subscription - * @returns {String} + * @param {object} subscription subscription + * @returns {string} html */ function renderSubscriptionRow(subscription) { + if (!subscription) { + return ` + (topic not found) +`; + } return ` - ${subscription.callback} - ${dateOrNot(subscription.created, 'Unknown')} - ${dateOrNot(subscription.verified, 'Never')} - ${dateOrNot(subscription.expires, 'Never')} + ${subscription.callback} + ${TemplateHelper.dateFormat(subscription.created, Message.EndOfTime, Message.BeginningOfTime, Message.Unknown)} + ${TemplateHelper.dateFormat(subscription.verified, Message.EndOfTime, Message.Never, Message.Never)} + ${TemplateHelper.dateFormat(subscription.expires, Message.Never, Message.BeginningOfTime, Message.Never)} ${!!subscription.secret} ${subscription.signatureAlgorithm} ${subscription.httpRemoteAddr} ${subscription.httpFrom} - ${dateOrNot(subscription.contentDelivered, 'Never')} + ${TemplateHelper.dateFormat(subscription.contentDelivered, Message.EndOfTime, Message.Never, Message.Never)} ${subscription.deliveryAttemptsSinceSuccess} - ${dateOrNot(subscription.deliveryNextAttempt, 'Next Publish')} + ${TemplateHelper.dateFormat(subscription.deliveryNextAttempt, Message.EndOfTime, Message.NextPublish, Message.NextPublish)} ${subscription.id} `; } @@ -131,7 +91,7 @@ function renderSubscriptionRow(subscription) { /** * Render a row of headers for subscription details. - * @returns {String} + * @returns {string} html */ function renderSubscriptionRowHeader() { return ` @@ -146,132 +106,61 @@ function renderSubscriptionRowHeader() { Content Delivered Content Delivery Failures Next Delivery - ID + Internal Id `; } /** - * Render the preamble for an HTML page, up through body. - * @param {Number} pagePathLevel number of paths below root this page is - * @param {String} pageTitle - * @param {String[]} headElements - * @returns - */ -function htmlHead(pagePathLevel, pageTitle, headElements = []) { - const rootPathPfx = '../'.repeat(pagePathLevel); - return ` -` + - headElements.map((e) => `${' '.repeat(2)}${e}`).join('\n') + ` - ${pageTitle} - - - `; -} - - -/** - * Closes remainder of HTML page body. - * @returns {String} - */ -function htmlTail() { - return ` -`; -} - - -/** - * Render a navigation link for the header section. - * @param {Object} nav - * @param {String} nav.href - * @param {String} nav.class - * @param {String} nav.text - * @returns {String} - */ -function renderNavLink(nav) { - return `
  • - ${nav.text} -
  • `; -} - - -/** - * Render the navigation header, and open the main section. - * @param {String} pageTitle - * @param {Object[]} navLinks - * @returns {String} + * Escape some xml things in strings. + * @param {string} string string to escape + * @returns {string} escaped */ -function htmlHeader(pageTitle, navLinks = []) { - return `
    -

    ${pageTitle}

    - -
    -
    `; +function xmlEscape(string) { + if (typeof string === 'number') { + return string; + } + if (typeof string !== 'string') { + return undefined; + } + // eslint-disable-next-line security/detect-object-injection + return string.replace(/[<>&'"]/, (c) => ({ + '<': '<', + '>': '>', + '&': '&', + '\'': ''', + '"': '"', + }[c])); } /** - * Close the main section and finish off with boilerplate. - * @returns {String} + * Add common site links to navigation header. + * @param {number} pagePathLevel depth from root + * @param {object} ctx context + * @param {object} options options */ -function htmlFooter() { - return `
    - `; -} - +function navLinks(pagePathLevel, ctx, options) { + if (!options.navLinks) { + options.navLinks = []; + } + const rootPath = '../'.repeat(pagePathLevel); -/** - * Render all parts of an HTML page. - * @param {Number} pagePathLevel - * @param {String} pageTitle - * @param {String[]} headElements - * @param {Object[]} navLinks - * @param {String[]} main - * @returns {String} - */ -function htmlTemplate(pagePathLevel, pageTitle, headElements = [], navLinks = [], main = []) { - return [ - htmlHead(pagePathLevel, pageTitle, headElements), - htmlHeader(pageTitle, navLinks), - ...main, - htmlFooter(), - htmlTail(), - ].join('\n'); + if (options.pageIdentifier !== 'admin') { + options.navLinks.push({ + text: 'Admin', + href: `${rootPath}admin/`, + }); + } } -module.exports = { - dateOrNot, - secondsToPeriod, - htmlHeader, - htmlFooter, - htmlHead, - htmlTail, - renderNavLink, +module.exports = Object.assign(Object.create(TemplateHelper), { + navLinks, + xmlEscape, renderTopicRowHeader, renderTopicRow, renderSubscriptionRowHeader, renderSubscriptionRow, - htmlTemplate, -}; \ No newline at end of file +});