diff --git a/config/default.js b/config/default.js
index c6906ef..267502e 100644
--- a/config/default.js
+++ b/config/default.js
@@ -21,10 +21,20 @@ module.exports = {
},
directory: path.join(__dirname, '../content-types'),
messages: {
+ content: {
+ title: 'Revisions for \'%id\'',
+ },
+ format: {
+ id: 'Content ID must be in UUID format',
+ revision: 'Revision must be a number',
+ },
missing: {
type: 'Content Type \'%type\' not found',
id: 'Content with ID \'%id\' in Content Type \'%type\' not found',
- revision: 'Revision for ID \'%id\' in Content Type \'%type\' not found',
+ revision: 'Revision \'%revision\' for ID \'%id\' in Content Type \'%type\' not found',
+ },
+ revisions: {
+ title: 'Revision \'%revision\' for \'%id\'',
},
},
},
diff --git a/lib/routes/content.js b/lib/routes/content.js
index 25e995c..41b66e3 100644
--- a/lib/routes/content.js
+++ b/lib/routes/content.js
@@ -9,12 +9,52 @@ const content = require('punchcard-content-types');
const multipart = require('connect-multiparty');
const uuid = require('uuid');
const _ = require('lodash');
+const isUUID = require('validator/lib/isUUID');
const utils = require('../utils');
const database = require('../database');
const multipartMiddleware = multipart();
+const check = {
+ id: (req) => {
+ if (!isUUID(req.params.id)) {
+ _.set(req.session, '404', {
+ message: config.content.messages.format.id,
+ safe: `/${config.content.base}`,
+ });
+
+ return false;
+ }
+
+ return true;
+ },
+ revision: (req) => {
+ if (!Number.isInteger(parseInt(req.params.revision, 10))) {
+ _.set(req.session, '404', {
+ message: config.content.messages.format.revision.replace('%revision', req.params.revision),
+ safe: `/${config.content.base}`,
+ });
+
+ return false;
+ }
+
+ return true;
+ },
+ type: (req, type) => {
+ if (type === false) {
+ _.set(req.session, '404', {
+ message: config.content.messages.missing.type.replace('%type', req.params.type),
+ safe: `/${config.content.base}`,
+ });
+
+ return false;
+ }
+
+ return true;
+ },
+};
+
/*
* Content Route Resolution
*
@@ -52,12 +92,8 @@ const routes = application => {
app.get(`/${config.content.base}/:type`, (req, res, next) => {
const type = utils.singleItem('id', req.params.type.toLowerCase(), types);
- if (type === false) {
- _.set(req.session, '404', {
- message: config.content.messages.missing.type.replace('%type', req.params.type),
- safe: `/${config.content.base}`,
- });
-
+ // check type exists
+ if (!check.type(req, type)) {
return next();
}
@@ -96,12 +132,8 @@ const routes = application => {
values = utils.config(values);
- if (type === false) {
- _.set(req.session, '404', {
- message: config.content.messages.missing.type.replace('%type', req.params.type),
- safe: `/${config.content.base}`,
- });
-
+ // check type exists
+ if (!check.type(req, type)) {
return next();
}
@@ -120,59 +152,137 @@ const routes = application => {
});
/*
- * @name Individual Content Type Edit Page
+ * @name Individual Piece of Content
*
* @param {object} req - HTTP Request
* @param {object} res - HTTP Response
* @param {object} next - Express callback
*/
- app.get(`/${config.content.base}/:type/:id/:revision/${config.content.actions.edit}`, (req, res, next) => {
+ app.get(`/${config.content.base}/:type/:id`, (req, res, next) => {
const type = utils.singleItem('id', req.params.type.toLowerCase(), types);
- const errors = _.get(req.session, `form.content.add[${req.params.type.toLowerCase()}].errors`, {});
- const idSess = _.get(req.session, `form.content.add[${req.params.type.toLowerCase()}].id`, {});
- const revisionSess = _.get(req.session, `form.content.add[${req.params.type.toLowerCase()}].revision`, {});
- let values = _.get(req.session, `form.content.add[${req.params.type.toLowerCase()}].content`, {});
- const data = {};
- let revisionSearch;
- _.unset(req.session, 'form.content.add');
+ // check type exists
+ if (!check.type(req, type)) {
+ return next();
+ }
- values = utils.config(values);
+ // id in url is not UUID
+ if (!check.id(req)) {
+ return next();
+ }
- // no content type in url, so 404
- if (type === false) {
- _.set(req.session, '404', {
- message: config.content.messages.missing.type.replace('%type', req.params.type),
- safe: `/${config.content.base}`,
+ type.primary = type.attributes[0].inputs[Object.keys(type.attributes[0].inputs)[0]].name;
+
+ return database
+ .select('*')
+ .from(`content-type--${type.id}`)
+ .where('id', req.params.id)
+ .orderBy('revision', 'DESC').then(rows => {
+ if (rows.length < 1) {
+ _.set(req.session, '404', {
+ message: config.content.messages.missing.id.replace('%type', req.params.type).replace('%id', req.params.id),
+ safe: `/${config.content.base}/${req.params.type}`,
+ });
+
+ return next();
+ }
+
+ res.render('content/content', {
+ title: config.content.messages.content.title.replace('%id', req.params.id),
+ content: rows,
+ type,
+ config: config.content,
+ });
+
+ return true;
});
+ });
+
+ /*
+ * @name Specific revision of an Individual Piece of Content
+ *
+ * @param {object} req - HTTP Request
+ * @param {object} res - HTTP Response
+ * @param {object} next - Express callback
+ */
+ app.get(`/${config.content.base}/:type/:id/:revision`, (req, res, next) => {
+ const type = utils.singleItem('id', req.params.type.toLowerCase(), types);
+
+ // check type exists
+ if (!check.type(req, type)) {
+ return next();
+ }
+
+ // id in url is not UUID
+ if (!check.id(req)) {
+ return next();
+ }
+ // revision in url is not a number
+ if (!check.revision(req)) {
return next();
}
- // no id in url, so 404
- if (!req.params.id) {
- _.set(req.session, '404', {
- message: config.content.messages.missing.id.replace('%type', req.params.type).replace('%id', req.params.id),
- safe: `/${config.content.base}/${req.params.type}`,
+ type.primary = type.attributes[0].inputs[Object.keys(type.attributes[0].inputs)[0]].name;
+
+ return database
+ .select('*')
+ .from(`content-type--${type.id}`)
+ .where('revision', req.params.revision)
+ .orderBy('revision', 'DESC').then(rows => {
+ if (rows.length < 1) {
+ _.set(req.session, '404', {
+ message: config.content.messages.missing.revision.replace('%revision', req.params.revision).replace('%type', req.params.type).replace('%id', req.params.id),
+ safe: `/${config.content.base}/${req.params.type}`,
+ });
+
+ return next();
+ }
+
+ res.render('content/content', {
+ title: config.content.messages.revisions.title.replace('%revision', req.params.revision).replace('%id', req.params.id),
+ content: rows,
+ type,
+ config: config.content,
+ });
+
+ return true;
});
+ });
+ /*
+ * @name Individual Content Type Edit Page
+ *
+ * @param {object} req - HTTP Request
+ * @param {object} res - HTTP Response
+ * @param {object} next - Express callback
+ */
+ app.get(`/${config.content.base}/:type/:id/:revision/${config.content.actions.edit}`, (req, res, next) => {
+ const type = utils.singleItem('id', req.params.type.toLowerCase(), types);
+ const errors = _.get(req.session, `form.content.add[${req.params.type.toLowerCase()}].errors`, {});
+ const idSess = _.get(req.session, `form.content.add[${req.params.type.toLowerCase()}].id`, {});
+ const revisionSess = _.get(req.session, `form.content.add[${req.params.type.toLowerCase()}].revision`, {});
+ let values = _.get(req.session, `form.content.add[${req.params.type.toLowerCase()}].content`, {});
+ const data = {};
+
+ // check type exists
+ if (!check.type(req, type)) {
return next();
}
- // no revision in url, so create search to find latest revision
- if (!req.params.revision || !Number.isInteger(req.params.revision)) {
- revisionSearch = database(`content-type--${type.id}`)
- .select('revision')
- .where('id', req.params.id)
- .orderBy('revision', 'DESC')
- .limit(1);
+ // id in url is not UUID
+ if (!check.id(req)) {
+ return next();
}
- else {
- revisionSearch = database(`content-type--${type.id}`)
- .select('revision')
- .where('revision', req.params.revision);
+
+ // revision in url is not a number
+ if (!check.revision(req)) {
+ return next();
}
+ _.unset(req.session, 'form.content.add');
+
+ values = utils.config(values);
// something went wrong on save:
if (Object.keys(values).length > 0) {
@@ -201,25 +311,13 @@ const routes = application => {
// eslint mad if no return, then mad at this else if it is there
else { // eslint-disable-line no-else-return
// Search for the revision
- return revisionSearch.then(rows => {
- if (rows.length < 1) {
- _.set(req.session, '404', {
- message: config.content.messages.missing.revision.replace('%type', req.params.type).replace('%id', req.params.id),
- safe: `/${config.content.base}/${req.params.type}`,
- });
-
- return next();
- }
- const revision = rows[0].revision;
-
- return database(`content-type--${type.id}`).where({
- id: req.params.id,
- revision,
- });
+ return database(`content-type--${type.id}`).where({
+ id: req.params.id,
+ revision: req.params.revision,
}).then(rows => {
if (rows.length < 1) {
_.set(req.session, '404', {
- message: config.content.messages.missing.id.replace('%type', req.params.type).replace('%id', req.params.id).replace('%revision', req.params.revision),
+ message: config.content.messages.missing.revision.replace('%type', req.params.type).replace('%id', req.params.id).replace('%revision', req.params.revision),
safe: `/${config.content.base}/${req.params.type}`,
});
@@ -282,12 +380,8 @@ const routes = application => {
app.post(`/${config.content.base}/:type/${config.content.actions.save}`, multipartMiddleware, (req, res, next) => {
const type = utils.singleItem('id', req.params.type.toLowerCase(), types);
- if (type === false) {
- _.set(req.session, '404', {
- message: config.content.messages.missing.type.replace('%type', req.params.type),
- safe: `/${config.content.base}`,
- });
-
+ // check type exists
+ if (!check.type(req, type)) {
return next();
}
diff --git a/views/content/content.html b/views/content/content.html
new file mode 100644
index 0000000..57a4b5d
--- /dev/null
+++ b/views/content/content.html
@@ -0,0 +1,41 @@
+{% extends "_donut.html" %}
+
+{% block pageHead %}
+ {{ super() }}
+{% endblock %}
+
+{% block header %}
+ {{ super() }}
+{% endblock %}
+
+{% block main %}
+
{{title}}
+
+Revisions
+
+{% if content %}
+
+
+
+ revision |
+ {{config.base}} |
+ {{config.actions.edit}} |
+ date |
+
+ {% for c in content %}
+ {% if c.value %}
+
+ {{c.revision}} |
+ {{c.value[type.primary]}} |
+ {{config.actions.edit}} |
+ {{c.created}} |
+
+ {% endif %}
+ {% endfor %}
+
+{% endif %}
+{% endblock %}
+
+{% block footer %}
+{{ super() }}
+{% endblock %}
diff --git a/views/content/revision.html b/views/content/revision.html
new file mode 100644
index 0000000..57a4b5d
--- /dev/null
+++ b/views/content/revision.html
@@ -0,0 +1,41 @@
+{% extends "_donut.html" %}
+
+{% block pageHead %}
+ {{ super() }}
+{% endblock %}
+
+{% block header %}
+ {{ super() }}
+{% endblock %}
+
+{% block main %}
+{{title}}
+
+Revisions
+
+{% if content %}
+
+
+
+ revision |
+ {{config.base}} |
+ {{config.actions.edit}} |
+ date |
+
+ {% for c in content %}
+ {% if c.value %}
+
+ {{c.revision}} |
+ {{c.value[type.primary]}} |
+ {{config.actions.edit}} |
+ {{c.created}} |
+
+ {% endif %}
+ {% endfor %}
+
+{% endif %}
+{% endblock %}
+
+{% block footer %}
+{{ super() }}
+{% endblock %}