diff --git a/app.js b/app.js
index c5507da2e..b3400b939 100644
--- a/app.js
+++ b/app.js
@@ -1,4 +1,6 @@
const express = require('express');
+const nunjucks = require('nunjucks');
+const cons = require('consolidate');
const trace = require('./middleware/trace');
const logging = require('./middleware/logging');
const path = require('path');
@@ -72,7 +74,26 @@ app.use(
// VIEW CONFIGURATION
//==============================================================================
-app.set('views', path.join(__dirname, 'views'));
+// configure the default views directory.
+const views = path.join(__dirname, 'views');
+app.set('views', views);
+
+// reconfigure nunjucks.
+cons.requires.nunjucks = nunjucks.configure(views, {
+ autoescape: true,
+ trimBlocks: true,
+ lstripBlocks: true,
+ watch: process.env.NODE_ENV === 'development',
+});
+
+// assign the nunjucks engine to .njk files.
+app.engine('njk', cons.nunjucks);
+
+// assign the ejs engine to .ejs and .html files.
+app.engine('ejs', cons.ejs);
+app.engine('html', cons.ejs);
+
+// set .ejs as the default extension.
app.set('view engine', 'ejs');
//==============================================================================
diff --git a/package.json b/package.json
index 97f4e373d..e8ff0182d 100644
--- a/package.json
+++ b/package.json
@@ -91,6 +91,7 @@
"common-tags": "^1.4.0",
"compression": "1.7.1",
"compression-webpack-plugin": "^1.0.0",
+ "consolidate": "0.14.0",
"cookie-parser": "^1.4.3",
"copy-webpack-plugin": "^4.0.0",
"cross-spawn": "^5.1.0",
diff --git a/plugins/talk-plugin-notifications/server/router.js b/plugins/talk-plugin-notifications/server/router.js
index 68919a936..9185c5aba 100644
--- a/plugins/talk-plugin-notifications/server/router.js
+++ b/plugins/talk-plugin-notifications/server/router.js
@@ -4,7 +4,7 @@ const { get, isEmpty, reduce } = require('lodash');
module.exports = router => {
router.get('/account/unsubscribe-notifications', (req, res) => {
- res.render(path.join(__dirname, 'views/unsubscribe-notifications'));
+ res.render(path.join(__dirname, 'views/unsubscribe-notifications.njk'));
});
/**
diff --git a/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.ejs b/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.ejs
deleted file mode 100644
index 129c3115c..000000000
--- a/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.ejs
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
- <%= t('talk-plugin-notifications.unsubscribe_page.unsubscribe') %>
- <%- include(root + '/partials/head') %>
-
-
-
-
-
-
<%= t('talk-plugin-notifications.unsubscribe_page.token_invalid') %>
-
<%= t('talk-plugin-notifications.unsubscribe_page.are_unsubscribed') %>
-
-
-
-
-
-
\ No newline at end of file
diff --git a/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.njk b/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.njk
new file mode 100644
index 000000000..f8191d525
--- /dev/null
+++ b/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.njk
@@ -0,0 +1,61 @@
+{% extends "templates/account.njk" %}
+
+{% block title %}{{ t('talk-plugin-notifications.unsubscribe_page.unsubscribe') }}{% endblock %}
+
+{% block html %}
+
+
{{ t('talk-plugin-notifications.unsubscribe_page.token_invalid') }}
+
{{ t('talk-plugin-notifications.unsubscribe_page.are_unsubscribed') }}
+
+
+{% endblock %}
+
+{% block js %}
+
+
+{% endblock %}
diff --git a/plugins/talk-plugin-profile-data/server/router.js b/plugins/talk-plugin-profile-data/server/router.js
index 6d8f47d03..6bd165e64 100644
--- a/plugins/talk-plugin-profile-data/server/router.js
+++ b/plugins/talk-plugin-profile-data/server/router.js
@@ -102,7 +102,7 @@ async function loadComments(ctx, userID, archive, latestContentDate) {
module.exports = router => {
// /account/download will render the download page.
router.get('/account/download', (req, res) => {
- res.render(path.join(__dirname, 'views', 'download'));
+ res.render(path.join(__dirname, 'views', 'download.njk'));
});
// /api/v1/account/download will send back a zipped archive of the users
diff --git a/plugins/talk-plugin-profile-data/server/views/download.ejs b/plugins/talk-plugin-profile-data/server/views/download.ejs
deleted file mode 100644
index 260badf3a..000000000
--- a/plugins/talk-plugin-profile-data/server/views/download.ejs
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
- <%= t('download_landing.download_your_account') %>
- <%- include(root + '/partials/account') %>
-
-
-
-
- <%= t('download_landing.download_your_account') %>
- <%= t('download_landing.download_details') %>
- <%= t('download_landing.all_information_included') %>
-
- - <%= t('download_landing.information_included.date') %>
- - <%= t('download_landing.information_included.url') %>
- - <%= t('download_landing.information_included.body') %>
- - <%= t('download_landing.information_included.asset_url') %>
-
-
-
-
-
-
-
-
-
diff --git a/plugins/talk-plugin-profile-data/server/views/download.njk b/plugins/talk-plugin-profile-data/server/views/download.njk
new file mode 100644
index 000000000..5527473bc
--- /dev/null
+++ b/plugins/talk-plugin-profile-data/server/views/download.njk
@@ -0,0 +1,56 @@
+{% extends "templates/account.njk" %}
+
+{% block title %}{{ t('download_landing.download_your_account') }}{% endblock %}
+
+{% block html %}
+
+
+ {{ t('download_landing.download_your_account') }}
+ {{ t('download_landing.download_details') }}
+ {{ t('download_landing.all_information_included') }}
+
+ - {{ t('download_landing.information_included.date') }}
+ - {{ t('download_landing.information_included.url') }}
+ - {{ t('download_landing.information_included.body') }}
+ - {{ t('download_landing.information_included.asset_url') }}
+
+
+
+
+
+{% endblock %}
+
+{% block js %}
+
+
+{% endblock %}
diff --git a/routes/account/index.js b/routes/account/index.js
index 70e62accc..37b26e344 100644
--- a/routes/account/index.js
+++ b/routes/account/index.js
@@ -2,11 +2,11 @@ const express = require('express');
const router = express.Router();
router.get('/email/confirm', (req, res) => {
- res.render('account/email/confirm');
+ res.render('account/email/confirm.njk');
});
router.get('/password/reset', (req, res) => {
- res.render('account/password/reset');
+ res.render('account/password/reset.njk');
});
module.exports = router;
diff --git a/routes/admin/index.js b/routes/admin/index.js
index d00ef8642..1a9993cfd 100644
--- a/routes/admin/index.js
+++ b/routes/admin/index.js
@@ -2,7 +2,7 @@ const express = require('express');
const router = express.Router();
router.get('*', (req, res) => {
- res.render('admin');
+ res.render('admin.njk');
});
module.exports = router;
diff --git a/routes/api/v1/graph.js b/routes/api/v1/graph.js
index 40e87415b..0660755ea 100644
--- a/routes/api/v1/graph.js
+++ b/routes/api/v1/graph.js
@@ -10,7 +10,7 @@ router.use('/ql', apollo.graphqlExpress(createGraphOptions));
if (process.env.NODE_ENV !== 'production') {
// Interactive graphiql interface.
router.use('/iql', staticTemplate, (req, res) => {
- res.render('api/graphiql', {
+ res.render('api/graphiql.njk', {
endpointURL: 'api/v1/graph/ql',
});
});
diff --git a/routes/dev/assets.js b/routes/dev/assets.js
index aea0628e3..df59f21a0 100644
--- a/routes/dev/assets.js
+++ b/routes/dev/assets.js
@@ -11,7 +11,7 @@ router.get('/id/:asset_id', async (req, res, next) => {
throw new ErrNotFound();
}
- res.render('dev/article', {
+ res.render('dev/article.njk', {
title: asset.title,
asset_id: asset.id,
asset_url: asset.url,
@@ -28,7 +28,7 @@ router.get('/random', (req, res) => {
});
router.get('/title/:asset_title', (req, res) => {
- res.render('dev/article', {
+ res.render('dev/article.njk', {
title: req.params.asset_title.split('-').join(' '),
asset_url: '',
asset_id: null,
@@ -48,7 +48,7 @@ router.get('/', async (req, res, next) => {
Asset.count(),
]);
- res.render('dev/articles', {
+ res.render('dev/articles.njk', {
skip,
limit,
count,
diff --git a/routes/dev/index.js b/routes/dev/index.js
index 58daca583..580e702fd 100644
--- a/routes/dev/index.js
+++ b/routes/dev/index.js
@@ -12,7 +12,7 @@ router.get('/', staticTemplate, async (req, res) => {
await SetupService.isAvailable();
return res.redirect(url.resolve(MOUNT_PATH, 'admin/install'));
} catch (e) {
- return res.render('dev/article', {
+ return res.render('dev/article.njk', {
title: 'Coral Talk',
asset_url: '',
asset_id: '',
diff --git a/routes/embed/index.js b/routes/embed/index.js
index 695ffe609..7456a5b71 100644
--- a/routes/embed/index.js
+++ b/routes/embed/index.js
@@ -2,7 +2,7 @@ const express = require('express');
const router = express.Router();
router.use('/stream', (req, res) => {
- res.render('embed/stream');
+ res.render('embed/stream.njk');
});
module.exports = router;
diff --git a/routes/login/index.js b/routes/login/index.js
index b69cb7f5c..e03690dd0 100644
--- a/routes/login/index.js
+++ b/routes/login/index.js
@@ -2,7 +2,7 @@ const express = require('express');
const router = express.Router();
router.get('*', (req, res) => {
- res.render('login');
+ res.render('login.njk');
});
module.exports = router;
diff --git a/services/passport.js b/services/passport.js
index e4813e419..4b96d2352 100644
--- a/services/passport.js
+++ b/services/passport.js
@@ -115,13 +115,13 @@ const HandleAuthPopupCallback = (req, res, next) => (err, user) => {
res.locals.encodeJSONForHTML = encodeJSONForHTML;
if (err) {
- return res.render('auth-callback', {
+ return res.render('auth-callback.njk', {
auth: { err, data: null },
});
}
if (!user) {
- return res.render('auth-callback', {
+ return res.render('auth-callback.njk', {
auth: { err: new ErrNotAuthorized(), data: null },
});
}
@@ -132,7 +132,7 @@ const HandleAuthPopupCallback = (req, res, next) => (err, user) => {
SetTokenForSafari(req, res, token);
// We logged in the user! Let's send back the user data.
- res.render('auth-callback', {
+ res.render('auth-callback.njk', {
auth: { err: null, data: { user, token } },
});
};
diff --git a/views/account/email/confirm.ejs b/views/account/email/confirm.ejs
deleted file mode 100644
index 47bc7773b..000000000
--- a/views/account/email/confirm.ejs
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
- <%= t('confirm_email.email_confirmation') %>
- <%- include ../../partials/account %>
-
-
-
-
- <%= t('confirm_email.email_confirmation') %>
- <%= t('confirm_email.click_to_confirm') %>
-
-
-
-
-
-
-
-
diff --git a/views/account/email/confirm.njk b/views/account/email/confirm.njk
new file mode 100644
index 000000000..6111cb21e
--- /dev/null
+++ b/views/account/email/confirm.njk
@@ -0,0 +1,63 @@
+{% extends "templates/account.njk" %}
+
+{% block title %}{{ t('confirm_email.email_confirmation') }}{% endblock %}
+
+{% block html %}
+
+
+ {{ t('confirm_email.email_confirmation') }}
+ {{ t('confirm_email.click_to_confirm') }}
+
+
+
+
+{% endblock %}
+
+{% block js %}
+
+
+{% endblock %}
diff --git a/views/account/password/reset.ejs b/views/account/password/reset.ejs
deleted file mode 100644
index c8b106139..000000000
--- a/views/account/password/reset.ejs
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
- <%= t('password_reset.set_new_password') %>
- <%- include ../../partials/account %>
-
-
-
-
- <%= t('password_reset.set_new_password') %>
- <%= t('password_reset.change_password_help') %>
-
-
-
-
-
-
-
-
diff --git a/views/account/password/reset.njk b/views/account/password/reset.njk
new file mode 100644
index 000000000..b6f227dea
--- /dev/null
+++ b/views/account/password/reset.njk
@@ -0,0 +1,85 @@
+{% extends "templates/account.njk" %}
+
+{% block title %}{{ t('password_reset.set_new_password') }}{% endblock %}
+
+{% block html %}
+
+
+ {{ t('password_reset.set_new_password') }}
+ {{ t('password_reset.change_password_help') }}
+
+
+
+
+{% endblock %}
+
+{% block js %}
+
+
+{% endblock %}
diff --git a/views/admin.ejs b/views/admin.ejs
deleted file mode 100644
index 89cc150f7..000000000
--- a/views/admin.ejs
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
- Talk - Coral Admin
- <%- include partials/head %>
-
-
-
- <%- include partials/custom-css %>
-
-
-
-
-
-
-
diff --git a/views/admin.njk b/views/admin.njk
new file mode 100644
index 000000000..4b5236424
--- /dev/null
+++ b/views/admin.njk
@@ -0,0 +1,14 @@
+{% extends "templates/base.njk" %}
+
+{% block title %}Talk Admin{% endblock %}
+
+{% block css %}
+
+
+
+{% endblock %}
+
+{% block js %}
+
+
+{% endblock %}
diff --git a/views/api/graphiql.ejs b/views/api/graphiql.ejs
deleted file mode 100644
index 8007569b3..000000000
--- a/views/api/graphiql.ejs
+++ /dev/null
@@ -1,125 +0,0 @@
-
-
-
-
-
- GraphiQL
-
- <%- include ../partials/dev %>
-
-
-
-
-
-
-
-
- <%- include ../partials/dev-nav %>
-
-
-
-
diff --git a/views/api/graphiql.njk b/views/api/graphiql.njk
new file mode 100644
index 000000000..b726918f3
--- /dev/null
+++ b/views/api/graphiql.njk
@@ -0,0 +1,126 @@
+{% extends "templates/development.njk" %}
+
+{% block title %}GraphiQL{% endblock %}
+
+{% block css %}
+{# Include the base development pieces #}
+{{ super() }}
+
+
+{% endblock %}
+
+{% block js %}
+
+
+
+
+
+{% endblock %}
+
+{% block html %}
+
+{% endblock %}
diff --git a/views/auth-callback.ejs b/views/auth-callback.ejs
deleted file mode 100644
index e4d2387b7..000000000
--- a/views/auth-callback.ejs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
- <%- include partials/data %>
-
-
-
-
-
-
diff --git a/views/auth-callback.njk b/views/auth-callback.njk
new file mode 100644
index 000000000..7119a6255
--- /dev/null
+++ b/views/auth-callback.njk
@@ -0,0 +1,10 @@
+
+
+
+ {% include "partials/data.njk" %}
+
+
+
+
+
+
diff --git a/views/dev/article.ejs b/views/dev/article.ejs
deleted file mode 100644
index 008d3989e..000000000
--- a/views/dev/article.ejs
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- <%= title %>
- <%- include ../partials/dev %>
-
-
- <%- include ../partials/dev-nav %>
-
-
-
diff --git a/views/dev/article.njk b/views/dev/article.njk
new file mode 100644
index 000000000..2fb8b3724
--- /dev/null
+++ b/views/dev/article.njk
@@ -0,0 +1,55 @@
+{% extends "templates/development.njk" %}
+
+{% block title %}{{ title }}{% endblock %}
+
+{% block meta %}
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block html %}
+
+{% endblock %}
+
+{% block js %}
+
+
+{% endblock %}
diff --git a/views/dev/articles.ejs b/views/dev/articles.ejs
deleted file mode 100644
index 1b07ed116..000000000
--- a/views/dev/articles.ejs
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
- All Assets
- <%- include ../partials/dev %>
-
-
- <%- include ../partials/dev-nav %>
-
-
-
All Assets
- <%= skip + 1 %> - <%= skip + assets.length %> of <%= count %> Assets
-
-
- <% if (count !== assets.length) { %>
-
- <% } %>
-
-
-
diff --git a/views/dev/articles.njk b/views/dev/articles.njk
new file mode 100644
index 000000000..e204c39fc
--- /dev/null
+++ b/views/dev/articles.njk
@@ -0,0 +1,37 @@
+{% extends "templates/development.njk" %}
+
+{% block title %}All Assets{% endblock %}
+
+{% block html %}
+
+
+
All Assets
+ {{ skip + 1 }} - {{ skip + assets.length }} of {{ count }} Assets
+
+
+ {% if count !== assets.length %}
+
+ {% endif %}
+
+{% endblock %}
diff --git a/views/embed/stream.ejs b/views/embed/stream.ejs
deleted file mode 100644
index a1c708b55..000000000
--- a/views/embed/stream.ejs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
- <%- include ../partials/head %>
-
-
- <%- include ../partials/custom-css %>
-
-
-
-
-
-
diff --git a/views/embed/stream.njk b/views/embed/stream.njk
new file mode 100644
index 000000000..ddc620f8e
--- /dev/null
+++ b/views/embed/stream.njk
@@ -0,0 +1,16 @@
+{% extends "templates/base.njk" %}
+
+{% block title %}Talk{% endblock %}
+
+{% block css %}
+
+
+{% endblock %}
+
+{% block html %}
+
+{% endblock %}
+
+{% block js %}
+
+{% endblock %}
diff --git a/views/login.ejs b/views/login.ejs
deleted file mode 100644
index 671b25c83..000000000
--- a/views/login.ejs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
- <%- include partials/head %>
-
- <%- include partials/custom-css %>
-
-
-
-
-
-
-
diff --git a/views/login.njk b/views/login.njk
new file mode 100644
index 000000000..2288898c7
--- /dev/null
+++ b/views/login.njk
@@ -0,0 +1,16 @@
+{% extends "templates/base.njk" %}
+
+{% block title %}Talk - Login{% endblock %}
+
+{% block css %}
+
+{% endblock %}
+
+{% block html %}
+
+{% endblock %}
+
+{% block js %}
+
+
+{% endblock %}
diff --git a/views/partials/account.ejs b/views/partials/account.ejs
deleted file mode 100644
index ff2166816..000000000
--- a/views/partials/account.ejs
+++ /dev/null
@@ -1,4 +0,0 @@
-<%- include head %>
-
-
-<%- include custom-css %>
diff --git a/views/partials/custom-css.ejs b/views/partials/custom-css.ejs
deleted file mode 100644
index d453c37e1..000000000
--- a/views/partials/custom-css.ejs
+++ /dev/null
@@ -1 +0,0 @@
-<% if (locals.customCssUrl) { %><% } %>
diff --git a/views/partials/custom-css.njk b/views/partials/custom-css.njk
new file mode 100644
index 000000000..6d725e31e
--- /dev/null
+++ b/views/partials/custom-css.njk
@@ -0,0 +1,3 @@
+{% if customCssUrl %}
+
+{% endif %}
diff --git a/views/partials/data.ejs b/views/partials/data.ejs
deleted file mode 100644
index f0bbf5925..000000000
--- a/views/partials/data.ejs
+++ /dev/null
@@ -1,3 +0,0 @@
-<%_ if (data != null) { _%>
-
-<%_ } _%>
\ No newline at end of file
diff --git a/views/partials/data.njk b/views/partials/data.njk
new file mode 100644
index 000000000..4edaf2e2e
--- /dev/null
+++ b/views/partials/data.njk
@@ -0,0 +1,3 @@
+{% if data %}
+
+{% endif %}
diff --git a/views/partials/dev-nav.ejs b/views/partials/dev-nav.ejs
deleted file mode 100644
index 088c52c20..000000000
--- a/views/partials/dev-nav.ejs
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/views/partials/favicon.njk b/views/partials/favicon.njk
new file mode 100644
index 000000000..5ec2aad8a
--- /dev/null
+++ b/views/partials/favicon.njk
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/views/partials/head.ejs b/views/partials/head.ejs
deleted file mode 100644
index dc3df7103..000000000
--- a/views/partials/head.ejs
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-<%- include data %>
-
-
diff --git a/views/partials/nav.njk b/views/partials/nav.njk
new file mode 100644
index 000000000..ecea9018d
--- /dev/null
+++ b/views/partials/nav.njk
@@ -0,0 +1,8 @@
+
diff --git a/views/templates/account.njk b/views/templates/account.njk
new file mode 100644
index 000000000..7e1b6e6c9
--- /dev/null
+++ b/views/templates/account.njk
@@ -0,0 +1,6 @@
+{% extends "templates/base.njk" %}
+
+{% block css %}
+
+
+{% endblock %}
diff --git a/views/templates/base.njk b/views/templates/base.njk
new file mode 100644
index 000000000..23048151e
--- /dev/null
+++ b/views/templates/base.njk
@@ -0,0 +1,39 @@
+
+
+
+ {# Meta tags #}
+
+
+ {% block meta %}{% endblock %}
+
+ {# Favicon Configuration #}
+ {% include "partials/favicon.njk" %}
+
+ {# CSP and security headers #}
+ {% block security %}{% endblock %}
+
+ {# Title #}
+ {% block title %}{% endblock %}
+
+ {# CSS #}
+
+
+
+ {% include "partials/custom-css.njk" %}
+ {% block css %}{% endblock %}
+
+ {# Static data injection #}
+ {% include "partials/data.njk" %}
+
+ {# Configuration #}
+
+
+
+ {% block body %}
+ {% block html %}
+
+ {% endblock %}
+ {% endblock %}
+ {% block js %}{% endblock %}
+
+
diff --git a/views/partials/dev.ejs b/views/templates/development.njk
similarity index 54%
rename from views/partials/dev.ejs
rename to views/templates/development.njk
index 691b548e5..f7fab6682 100644
--- a/views/partials/dev.ejs
+++ b/views/templates/development.njk
@@ -1,5 +1,17 @@
-
+{% extends "templates/base.njk" %}
+
+{# Null out the security block, we don't want/need that in development #}
+{% block security %}{% endblock %}
+
+{% block css %}
-
+
+{% endblock %}
+
+{% block body %}
+ {% include "partials/nav.njk" %}
+
+ {% block html %}{% endblock %}
+{% endblock %}
diff --git a/yarn.lock b/yarn.lock
index f0da6d006..770dcc7ec 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2392,6 +2392,12 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+consolidate@0.14.0:
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.14.0.tgz#b03acd566a2565ca96e99f44fd1417486b4df88d"
+ dependencies:
+ bluebird "^3.1.1"
+
constantinople@^3.0.1:
version "3.1.0"
resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-3.1.0.tgz#7569caa8aa3f8d5935d62e1fa96f9f702cd81c79"