diff --git a/library.js b/library.js index 518ebcb3..d191c11e 100644 --- a/library.js +++ b/library.js @@ -16,17 +16,47 @@ const groups = require.main.require('./src/groups'); const Comments = module.exports; +// CORS Middleware +const CORSMiddleware = function (req, res, next) { + const hostUrls = (meta.config['blog-comments:url'] || '').split(','); + const url = hostUrls.find((hostUrl) => { + hostUrl = hostUrl.trim(); + if (hostUrl[hostUrl.length - 1] === '/') { + hostUrl = hostUrl.substring(0, hostUrl.length - 1); + } + + return (hostUrl === req.get('origin')); + }); + + if (url) { + res.header('Access-Control-Allow-Origin', req.get('origin')); + } else { + winston.warn(`[nodebb-plugin-blog-comments] Origin (${req.get('origin')}) does not match hostUrls: ${hostUrls.join(', ')}`); + } + + res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT'); + res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept'); + res.header('Access-Control-Allow-Credentials', 'true'); + + next(); +}; + Comments.init = async function (params) { const { router, middleware } = params; const routeHelpers = require.main.require('./src/routes/helpers'); Comments.template = await fs.promises.readFile(path.resolve(__dirname, './public/templates/comments/comments.tpl'), { encoding: 'utf-8' }); - router.get('/comments/get/:id/:pagination?', middleware.applyCSRF, routeHelpers.tryRoute(Comments.getCommentData)); - router.post('/comments/reply', middleware.applyCSRF, routeHelpers.tryRoute(Comments.replyToComment)); - router.post('/comments/publish', middleware.applyCSRF, routeHelpers.tryRoute(Comments.publishArticle)); + const middlewares = [ + CORSMiddleware, + middleware.applyCSRF, + ]; + + router.get('/comments/get/:id/:pagination?', middlewares, middleware.pluginHooks, routeHelpers.tryRoute(Comments.getCommentData)); + router.post('/comments/reply', middlewares, routeHelpers.tryRoute(Comments.replyToComment)); + router.post('/comments/publish', middlewares, routeHelpers.tryRoute(Comments.publishArticle)); - routeHelpers.setupAdminPageRoute(router, '/admin/blog-comments', middleware, [], (req, res) => { + routeHelpers.setupAdminPageRoute(router, '/admin/blog-comments', middleware, [], (_, res) => { res.render('comments/admin', {}); }); }; @@ -35,49 +65,31 @@ Comments.getTopicIDByCommentID = async function (commentID) { return await db.getObjectField('blog-comments', commentID); }; -Comments.getCommentData = async function (req, res, next) { +Comments.getCommentData = async function (req, res) { const commentID = req.params.id; const pagination = req.params.pagination ? req.params.pagination : 0; const tid = await Comments.getTopicIDByCommentID(commentID); const topicData = await topics.getTopicData(tid); - if (!topicData) { - return next(); - } + const start = pagination * 10; const stop = start + 9; - const [postData, userData, isAdmin, isPublisher, categoryData, mainPost] = await Promise.all([ - topics.getTopicPosts(topicData, `tid:${tid}:posts`, start, stop, req.uid, true), + const [userData, isAdmin, isPublisher] = await Promise.all([ user.getUserData(req.uid), user.isAdministrator(req.uid), groups.isMember(req.uid, 'publishers'), - topics.getCategoryData(tid), - topics.getMainPost(tid, req.uid), ]); - const hostUrls = (meta.config['blog-comments:url'] || '').split(','); - let url; - - hostUrls.forEach((hostUrl) => { - hostUrl = hostUrl.trim(); - if (hostUrl[hostUrl.length - 1] === '/') { - hostUrl = hostUrl.substring(0, hostUrl.length - 1); - } - - if (hostUrl === req.get('origin')) { - url = req.get('origin'); - } - }); - - if (url) { - res.header('Access-Control-Allow-Origin', url); - } else { - winston.warn(`[nodebb-plugin-blog-comments] Origin (${req.get('origin')}) does not match hostUrls: ${hostUrls.join(', ')}`); + let postData = []; let categoryData = null; let + mainPost = null; + if (topicData) { + [postData, categoryData, mainPost] = await Promise.all([ + topics.getTopicPosts(topicData, `tid:${tid}:posts`, start, stop, req.uid, true), + topics.getCategoryData(tid), + topics.getMainPost(tid, req.uid), + ]); } - res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept'); - res.header('Access-Control-Allow-Credentials', 'true'); - const posts = postData.filter((post) => { if (post.user && post.user.picture && !post.user.picture.startsWith('http')) { post.user.picture = post.user.picture.replace(relativePath, ''); @@ -88,23 +100,29 @@ Comments.getCommentData = async function (req, res, next) { if (userData.picture && !userData.picture.startsWith('http')) { userData.picture = userData.picture.replace(relativePath, ''); } + const compose_location = meta.config['blog-comments:compose-location'] || 'top'; - const top = compose_location === 'top'; - const bottom = compose_location === 'bottom'; + const show_branding = (meta.config['blog-comments:show-branding'] || 'on') === 'on'; + const atTop = compose_location === 'top'; res.json({ - posts: posts, - postCount: topicData.postcount, + posts, + postCount: topicData ? topicData.postcount : 0, user: userData, template: Comments.template, token: req.csrfToken && req.csrfToken(), - isAdmin: !isAdmin ? isPublisher : isAdmin, + isAdmin: isAdmin || isPublisher, isLoggedIn: req.loggedIn, - tid: tid, + tid, category: categoryData, - mainPost: mainPost, - atTop: top, - atBottom: bottom, + mainPost, + atTop, + atBottom: !atTop, + show_branding, + loginURL: meta.config['blog-comments:login-url'] || '', + registerURL: meta.config['blog-comments:register-url'] || '', + authFlow: meta.config['blog-comments:auth-behavior'] || 'popup', + autoCreate: meta.config['blog-comments:autocreate'] === 'on', }); }; @@ -152,12 +170,19 @@ Comments.publishArticle = async function (req, res) { groups.isMember(req.uid, 'publishers'), ]); - if (!isAdmin && !isPublisher) { + let { uid } = req; + if (meta.config['blog-comments:autocreate'] === 'on') { + uid = parseInt(meta.config['blog-comments:autocreate-user-id'], 10); + if (!uid) { + return res.json({ error: 'Invalid autocreate user specified' }); + } + } else if (!isAdmin && !isPublisher) { return res.json({ error: 'Only Administrators or members of the publishers group can publish articles' }); } + try { const result = await topics.post({ - uid: req.uid, + uid, title: title, content: markdown, tags: tags ? JSON.parse(tags) : [], @@ -167,7 +192,7 @@ Comments.publishArticle = async function (req, res) { if (result && result.postData && result.postData.tid) { await posts.setPostField(result.postData.pid, 'blog-comments:url', url); await db.setObjectField('blog-comments', commentID, result.postData.tid); - res.redirect(`${(req.header('Referer') || '/')}#nodebb-comments`); + res.redirect(`${url || req.header('Referer') || '/'}#nodebb-comments`); } } catch (err) { res.json({ error: `Unable to post topic ${err.message}` }); diff --git a/public/lib/general.js b/public/lib/general.js index f606ee0d..8bb8e79c 100644 --- a/public/lib/general.js +++ b/public/lib/general.js @@ -1,54 +1,70 @@ -(function() { - "use strict"; - - var articlePath = window.location.protocol + '//' + window.location.host + window.location.pathname; +'use strict'; - var pluginURL = nodeBBURL + '/plugins/nodebb-plugin-blog-comments', - savedText, nodebbDiv, contentDiv, commentsDiv, commentsCounter, commentsAuthor, commentsCategory; +/* eslint-disable no-undef */ +(function () { + const articlePath = `${window.location.protocol}//${window.location.host}${window.location.pathname}`; - var stylesheet = document.createElement("link"); - stylesheet.setAttribute("rel", "stylesheet"); - stylesheet.setAttribute("type", "text/css"); - stylesheet.setAttribute("href", pluginURL + '/css/comments.css'); + const pluginURL = `${nodeBBURL}/plugins/nodebb-plugin-blog-comments`; + let savedText; let contentDiv; let commentsDiv; let commentsCounter; let commentsAuthor; let + commentsCategory; - document.getElementsByTagName("head")[0].appendChild(stylesheet); + const stylesheet = document.createElement('link'); + stylesheet.setAttribute('rel', 'stylesheet'); + stylesheet.setAttribute('type', 'text/css'); + stylesheet.setAttribute('href', `${pluginURL}/css/comments.css`); + + document.getElementsByTagName('head')[0].appendChild(stylesheet); document.getElementById('nodebb-comments').insertAdjacentHTML('beforebegin', '
'); - nodebbDiv = document.getElementById('nodebb'); + const nodebbDiv = document.getElementById('nodebb'); function newXHR() { try { - return XHR = new XMLHttpRequest(); - } catch (e) { - try { - return XHR = new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { - return XHR = new ActiveXObject("Msxml2.XMLHTTP"); - } - } + return new XMLHttpRequest(); + } catch (e) { + try { + return new ActiveXObject('Microsoft.XMLHTTP'); + } catch (e) { + return new ActiveXObject('Msxml2.XMLHTTP'); + } + } } - var XHR = newXHR(), pagination = 0, modal; - - function authenticate(type) { - savedText = contentDiv.value; - modal = window.open(nodeBBURL + "/" + type + "/#blog/authenticate","_blank","toolbar=no, scrollbars=no, resizable=no, width=600, height=675"); - var timer = setInterval(function() { - if(modal.closed) { - clearInterval(timer); - pagination = 0; - reloadComments(); - } - }, 500); - } + const XHR = newXHR(); let pagination = 0; function normalizePost(post) { - return post.replace(/href="\/(?=\w)/g, 'href="' + nodeBBURL + '/') - .replace(/src="\/(?=\w)/g, 'src="' + nodeBBURL + '/'); + return post.replace(/href="\/(?=\w)/g, `href="${nodeBBURL}/`) + .replace(/src="\/(?=\w)/g, `src="${nodeBBURL}/`); + } + + function getArticleData() { + if (!articleData) { + console.error('Declare articleData variable!'); + return; + } + + const tags = (articleData.tags || []).map(tag => tag.title); + const { url } = articleData; + const title = articleData.title_plain; + const cid = articleData.cid || -1; + + const translator = document.createElement('span'); + translator.innerHTML = articleData.markDownContent; + const markdown = [ + translator.firstChild.innerHTML, + articleData.url && articleData.url.length ? `\n\n**Click [here](${url}) to see the full blog post**` : '', + ].join(''); + + return [markdown, title, cid, tags]; } - XHR.onload = function() { + XHR.onload = function () { + if (XHR.status === 302) { + reloadComments(); + return; + } if (XHR.status >= 200 && XHR.status < 400) { - var data = JSON.parse(XHR.responseText), html; + const data = JSON.parse(XHR.responseText); let + html; commentsDiv = document.getElementById('nodebb-comments-list'); commentsCounter = document.getElementById('nodebb-comments-count'); @@ -56,66 +72,62 @@ commentsCategory = document.getElementById('nodebb-comments-category'); data.relative_path = nodeBBURL; - data.redirect_url = articlePath; + data.redirect_url = (articleData && articleData.url) ? articleData.url : articlePath; data.article_id = articleID; data.pagination = pagination; data.postCount = parseInt(data.postCount, 10); - for (var post in data.posts) { - if (data.posts.hasOwnProperty(post)) { - data.posts[post].timestamp = timeAgo(parseInt(data.posts[post].timestamp), 10); - if (data.posts[post]['blog-comments:url']) { - delete data.posts[post]; - } + data.posts.forEach((_, i) => { + data.posts[i].timestamp = timeAgo(parseInt(data.posts[i].timestamp, 10)); + if (data.posts[i]['blog-comments:url']) { + delete data.posts[i]; } - } - + }); + if (commentsCounter) { commentsCounter.innerHTML = data.postCount ? (data.postCount - 1) : 0; } if (commentsCategory) { - commentsCategory.innerHTML = '' + data.category.name + ''; + commentsCategory.innerHTML = `${data.category.name}`; } if (commentsAuthor) { - commentsAuthor.innerHTML = ' ' + data.mainPost.user.username + ''; + commentsAuthor.innerHTML = ` ${data.mainPost.user.username}`; } if (pagination) { - html = normalizePost(parse(data, templates.blocks['posts'])); - commentsDiv.innerHTML = commentsDiv.innerHTML + html; + html = window.templates.parse(window.templates.getBlock(data.template, 'posts'), data); + commentsDiv.innerHTML += normalizePost(html); } else { - html = parse(data, data.template); + html = window.templates.parse(data.template, data); nodebbDiv.innerHTML = normalizePost(html); } contentDiv = document.getElementById('nodebb-content'); - setTimeout(function() { - var lists = nodebbDiv.getElementsByTagName("li"); - for (var list in lists) { - if (lists.hasOwnProperty(list)) { - lists[list].className = ''; - } - } + setTimeout(() => { + const lists = Array.from(nodebbDiv.getElementsByTagName('li')); + lists.forEach((_, i) => { + lists[i].className = ''; + }); }, 100); - + if (savedText) { contentDiv.value = savedText; } - if (data.tid) { - var loadMore = document.getElementById('nodebb-load-more'); - loadMore.onclick = function() { - pagination++; + if (data.mainPost) { + const loadMore = document.getElementById('nodebb-load-more'); + loadMore.onclick = function () { + pagination += 1; reloadComments(); - } + }; if (data.posts.length) { - loadMore.style.display = 'inline-block'; + loadMore.style.display = 'inline-block'; } - if (pagination * 10 + data.posts.length + 1 >= data.postCount) { + if ((pagination * 10) + data.posts.length + 1 >= data.postCount) { loadMore.style.display = 'none'; } @@ -124,7 +136,7 @@ } if (data.user && data.user.uid) { - var error = window.location.href.match(/error=[\w-]*/); + let error = window.location.href.match(/error=[\w-]*/); if (error) { error = error[0].split('=')[1]; if (error === 'too-many-posts') { @@ -134,57 +146,80 @@ } document.getElementById('nodebb-error').innerHTML = error; - } + } } else { - document.getElementById('nodebb-register').onclick = function() { - authenticate('register'); + const authenticate = data.authFlow !== 'redirect' ? function (url) { + savedText = contentDiv.value; + const modal = window.open(url, '_blank', 'toolbar=no, scrollbars=no, resizable=no, width=600, height=675'); + const timer = setInterval(() => { + if (modal.closed) { + clearInterval(timer); + pagination = 0; + reloadComments(); + } + }, 500); + } : function (url) { + location.href = `${url}?callbackUrl=${data.redirect_url}`; }; - document.getElementById('nodebb-login').onclick = function() { - authenticate('login'); - } + document.getElementById('nodebb-register').onclick = function () { + authenticate(data.registerURL && data.registerURL.length ? data.registerURL : `${nodeBBURL}/register/#blog/authenticate`); + }; + + document.getElementById('nodebb-login').onclick = function () { + authenticate(data.loginURL && data.loginURL.length ? data.loginURL : `${nodeBBURL}/login/#blog/authenticate`); + }; } - } else { - if (data.isAdmin) { - if (articleData) { - var translator = document.createElement('span'), - gTags = articleData.tags, - url = articleData.url, - title= articleData.title_plain, - cid = articleData.cid || -1, - tags = []; - translator.innerHTML = articleData.markDownContent; - - var markdown = translator.firstChild.innerHTML + '\n\n**Click [here]('+ url +') to see the full blog post**'; - - for (var tag in gTags) { - if (gTags.hasOwnProperty(tag)) { - tags.push(gTags[tag].title); - } - } - document.getElementById('nodebb-content-markdown').value = markdown; - document.getElementById('nodebb-content-title').value = title; - document.getElementById('nodebb-content-cid').value = cid; - document.getElementById('nodebb-content-tags').value = JSON.stringify(tags); - } else { - console.error('Declare articleData variable!'); - } + } else if (data.autoCreate) { + const [markdown, title, cid, tags] = getArticleData(); + if (!markdown.length || !title.length) { + console.error('Need a title and content to auto-create a topic.'); + return; } + + const formValues = { + markdown, + title, + cid, + tags: JSON.stringify(tags), + id: data.article_id, + url: data.redirect_url, + _csrf: data.token, + }; + + const formData = Object.keys(formValues).map(name => `${encodeURIComponent(name)}=${encodeURIComponent(formValues[name])}`).join('&'); + + const postXHR = newXHR(); + postXHR.onreadystatechange = function () { + if (postXHR.readyState === postXHR.DONE) { + reloadComments(); + } + }; + postXHR.open('POST', `${data.relative_path}/comments/publish`, true); + postXHR.withCredentials = true; + postXHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + postXHR.send(formData); + } else if (data.isAdmin) { + const [markdown, title, cid, tags] = getArticleData(); + + document.getElementById('nodebb-content-markdown').value = markdown; + document.getElementById('nodebb-content-title').value = title; + document.getElementById('nodebb-content-cid').value = cid; + document.getElementById('nodebb-content-tags').value = JSON.stringify(tags); } } }; function reloadComments() { - XHR.open('GET', nodeBBURL + '/comments/get/' + articleID + '/' + pagination, true); + XHR.open('GET', `${nodeBBURL}/comments/get/${articleID}/${pagination}`, true); XHR.withCredentials = true; XHR.send(); } reloadComments(); - - function timeAgo(time){ - var time_formats = [ + function timeAgo(time) { + const time_formats = [ [60, 'seconds', 1], [120, '1 minute ago'], [3600, 'minutes', 60], @@ -197,162 +232,29 @@ [4838400, 'last month'], [29030400, 'months', 2419200], [58060800, 'last year'], - [2903040000, 'years', 29030400] + [2903040000, 'years', 29030400], ]; - var seconds = (+new Date() - time) / 1000; + const seconds = (+new Date() - time) / 1000; if (seconds < 10) { return 'just now'; } - - var i = 0, format; - while (format = time_formats[i++]) { - if (seconds < format[0]) { - if (!format[2]) { - return format[1]; - } else { - return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ago'; - } - } - } - return time; - } - - var templates = {blocks: {}}; - function parse (data, template) { - function replace(key, value, template) { - var searchRegex = new RegExp('{' + key + '}', 'g'); - return template.replace(searchRegex, value); - } - function makeRegex(block) { - return new RegExp("[\\s\\S]*", 'g'); + const format = time_formats.find(time => seconds < time[0]); + if (!format) { + return time; } - - function makeConditionalRegex(block) { - return new RegExp("([\\s\\S]*?)", 'g'); - } - - function getBlock(regex, block, template) { - data = template.match(regex); - if (data == null) return; - - if (block !== undefined) templates.blocks[block] = data[0]; - - var begin = new RegExp("(\r\n)*(\r\n)*", "g"), - end = new RegExp("(\r\n)*(\r\n)*", "g"), - - data = data[0] - .replace(begin, "") - .replace(end, ""); - - return data; + if (!format[2]) { + return format[1]; } - function setBlock(regex, block, template) { - return template.replace(regex, block); - } - - var regex, block; - - return (function parse(data, namespace, template, blockInfo) { - if (!data || data.length == 0) { - template = ''; - } - - function checkConditional(key, value) { - var conditional = makeConditionalRegex(key), - matches = template.match(conditional); - - if (matches !== null) { - for (var i = 0, ii = matches.length; i < ii; i++) { - var conditionalBlock = matches[i].split(//); - - var statement = new RegExp("()|()", 'gi'); - - if (conditionalBlock[1]) { - // there is an else statement - if (!value) { - template = template.replace(matches[i], conditionalBlock[1].replace(statement, '')); - } else { - template = template.replace(matches[i], conditionalBlock[0].replace(statement, '')); - } - } else { - // regular if statement - if (!value) { - template = template.replace(matches[i], ''); - } else { - template = template.replace(matches[i], matches[i].replace(statement, '')); - } - } - } - } - } - - for (var d in data) { - if (data.hasOwnProperty(d)) { - if (typeof data[d] === 'undefined') { - continue; - } else if (data[d] === null) { - template = replace(namespace + d, '', template); - } else if (data[d].constructor == Array) { - checkConditional(namespace + d + '.length', data[d].length); - checkConditional('!' + namespace + d + '.length', !data[d].length); - - namespace += d + '.'; - - var regex = makeRegex(d), - block = getBlock(regex, namespace.substring(0, namespace.length - 1), template); - - if (block == null) { - namespace = namespace.replace(d + '.', ''); - continue; - } - - var numblocks = data[d].length - 1, - i = 0, - result = ""; - - do { - result += parse(data[d][i], namespace, block, {iterator: i, total: numblocks}); - } while (i++ < numblocks); - - namespace = namespace.replace(d + '.', ''); - template = setBlock(regex, result, template); - } else if (data[d] instanceof Object) { - template = parse(data[d], d + '.', template); - } else { - var key = namespace + d, - value = typeof data[d] === 'string' ? data[d].replace(/^\s+|\s+$/g, '') : data[d]; - - checkConditional(key, value); - checkConditional('!' + key, !value); - - if (blockInfo && blockInfo.iterator) { - checkConditional('@first', blockInfo.iterator === 0); - checkConditional('!@first', blockInfo.iterator !== 0); - checkConditional('@last', blockInfo.iterator === blockInfo.total); - checkConditional('!@last', blockInfo.iterator !== blockInfo.total); - } - - template = replace(key, value, template); - } - } - } - - if (namespace) { - var regex = new RegExp("{" + namespace + "[\\s\\S]*?}", 'g'); - template = template.replace(regex, ''); - namespace = ''; - } else { - // clean up all undefined conditionals - template = template.replace(//gi, 'ENDIF -->') - .replace(//gi, ''); - } + return `${Math.floor(seconds / format[2])} ${format[1]} ago`; + } +}()); - return template; - })(data, "", template); - } -}()); \ No newline at end of file +// https://raw.githubusercontent.com/benchpressjs/benchpressjs/v0.3.1/lib/templates.js +/* eslint-disable */ +!function(){const e={cache:{},globals:{}},t={};let n,o;const r={nestedConditionals:/(?!^)(?!$)/g,conditionalBlock:/[\r\n?\n]*?[\r\n?\n]*?/,conditionalHelper://,conditionalArgs:/[ ]*,[ ]*/,innerLoop:/\s*/g,removeTabspace:/^\t*?|^\r\n?\t*?|\t?$|\r\n?\t*?$/g,removeWhitespace:/(^[\r\n?|\n]*)|([\r\n\t]*$)/g,cleanupEmptyLoops:/\s*/g,cleanupMissingKeys:/[\r\n]*?[\/]?\{[a-zA-Z0-9\.]+[\r\n]*?\}/g,leftoverRoot:/\.\.\/([\S]*?)[\}| ]/g,getUndefinedKeys://g,backReferenceFix:/\$+/g,escapeBlocks://g,escapeKeys:/\{([\s\S]*?)\}/g,rootKey:/\{\.\.\/([\S]*?)\}/g,rootConditional:/IF \.\.\/([\S]*?)/g,rootConditionalFalse:/IF !\.\.\/([\S]*?)/g};"undefined"!=typeof self&&self.addEventListener&&self.addEventListener("message",t=>{if(!(t&&t.data&&t.data.template&&t.data.object))return;const{data:n}=t;self.postMessage({result:n.block?e.parse(n.template,n.block,n.object):e.parse(n.template,n.object),signature:n.signature},"*")},!1);const c={};let l=0;const s=2**53-1;function i(e,t,n,r){o?function(e,t,n,r){++l>s&&(l=0),t=function(e){for(const t in e)e.hasOwnProperty(t)&&"function"!=typeof e[t]||delete e[t];return e}(t),o.postMessage({template:e,object:t,block:n,signature:l}),c[l]=function(e){r(e),delete c[l]}}(e,t,n,r):r(a(n,e,t))}function a(t,n,o){return x(n=b(t=t?e.getBlock(n,t):n,o),n,o)}function f(e,t,n){return e.replace(t,n.toString().replace(r.backReferenceFix,"$$$"))}function u(e,t,n){let o;return n=n.toString(),o=f(e,new RegExp(`{{${t}}}`,"g"),n),f(o,new RegExp(`{${t}}`,"g"),n.replace(r.escapeKeys,"{$1}").replace(r.escapeBlocks,"<!--$1-->"))}function p(e,t){return t=`(${t})?`,new RegExp(`(\x3c!-- BEGIN ${t}${e} --\x3e[\\r\\n?|\\n]?)|(\x3c!-- END ${t}${e} --\x3e)`,"g")}function g(e){return new RegExp(`\x3c!-- IF ${e} --\x3e([\\s\\S]*?)\x3c!-- ENDIF ${e.split(",")[0]} --\x3e`,"g")}function d(e,t,n){return h(h(e,`!${t}`,!n),t,n)}function h(e,t,n){const o=e.match(g(t));if(o){const c=function(e){return new RegExp(`(\x3c!-- IF ${e} --\x3e)|(\x3c!-- ENDIF ${e.split(",")[0]} --\x3e)`,"g")}(t);for(let t=0,l=o.length;t{c[e.data.signature]&&c[e.data.signature](e.data.result)},!1)}catch(e){}},e.parse=function(t,o,r,c){if("string"!=typeof o&&(c=r,r=o,o=!1),!t)return c?c(""):"";if(r=function(t){for(const n in e.globals)e.globals.hasOwnProperty(n)&&(t[n]=t[n]||e.globals[n]);return t}(r||{}),n&&c)e.cache[t]?i(e.cache[t],r,o,c):n(t,n=>{e.cache[t]=n,i(n,r,o,c)});else{if(!c)return a(o,t,r);i(t,r,o,c)}},e.registerHelper=function(e,n){t[e]=n},e.registerLoader=function(e){n=e},e.setGlobal=function(t,n){e.globals[t]=n},e.getBlock=function(e,t){return e.replace(new RegExp(`[\\s\\S]*(\x3c!-- BEGIN ${t} --\x3e[\\s\\S]*?\x3c!-- END ${t} --\x3e)[\\s\\S]*`,"g"),"$1")},e.flush=function(){e.cache={}},"undefined"!=typeof module&&(module.exports=e),"undefined"!=typeof window&&(window.templates=e)}(); +/* eslint-enable */ diff --git a/public/templates/comments/admin.tpl b/public/templates/comments/admin.tpl index 0ee43712..6d0c0cc3 100644 --- a/public/templates/comments/admin.tpl +++ b/public/templates/comments/admin.tpl @@ -5,16 +5,59 @@

Comma-separated for more than one blog -
+ +

Comma-separated for more than one blog -
+ +

You may optionally comma-separate these values, or just use one category ID to post all blogs in one category -
+ +

You may place the composer on top or on the bottom of the comment thread +
+ +
+ Show NodeBB link and thread link after comments. +
+ +
+ +
+ If no article topic exists, auto-publish articles when accessed. + +
+ +
+ If auto create is on, which user should own the post? + +
+ +
+ When authenticating, should it be a pop-up or redirect? +
+ +
+ +
+ +
+ +
+ +
diff --git a/public/templates/comments/comments.tpl b/public/templates/comments/comments.tpl index 75ca202a..dd669286 100644 --- a/public/templates/comments/comments.tpl +++ b/public/templates/comments/comments.tpl @@ -1,4 +1,4 @@ - +
@@ -30,16 +30,16 @@
-
{user.username} commented {posts.timestamp}
{posts.content}
+
{posts.user.username} commented {posts.timestamp}
{posts.content}
@@ -73,7 +73,10 @@ - Powered by NodeBBView original thread + + Powered by NodeBBView original thread + + Commenting has been disabled. @@ -89,4 +92,4 @@ - +