punchcard-cms/punchcard

View on GitHub
lib/routes/workflows.js

Summary

Maintainability
D
2 days
Test Coverage
'use strict';

/*
 * @fileoverview Content system routing
 *
 */
const config = require('config');
const types = require('punchcard-content-types');
const _ = require('lodash');
const moment = require('moment-timezone');

const utils = require('../utils');
const database = require('../database');
const workflows = require('../workflows');
const schedule = require('../schedule');

/*
 * Content Route Resolution
 *
 * @param {object} application - Express Application
 * @returns {object} - Configured Express Application
 */
const routes = application => {
  return new Promise(resolve => {
    const app = application;
    const references = app.get('references');

    /*
     * @name Individual Content Type Approval 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.approve}`, (req, res, next) => {
      const errors = _.get(req.session, `form.content.approve[${req.params.type.toLowerCase()}].errors`, {});
      const values = _.get(req.session, `form.content.approve[${req.params.type.toLowerCase()}].content`, {});

      const steps = _.cloneDeep(req.content.workflow).steps.reverse();

      _.unset(req.session, 'form.content.approve');

      return utils.fill(req.content.types, req.content.type, references, database).then(ct => {
        return database(`content-type--${ct.id}`)
          .select('*')
          .where('revision', req.params.revision)
          .where('id', req.params.id)
          .then(rws => {
            if (rws.length < 1) {
              const err = {
                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}`,
                status: 404,
              };

              return next(err);
            }

            // add the previous session data back in
            _.set(req.session, 'form.content.approve', {
              [req.params.type.toLowerCase()]: {
                revision: req.params.revision,
                id: req.params.id,
              },
            });

            // add itentifier to each row
            const rows = utils.routes.identifier(rws, ct);

            const revision = rows[0];

            // grab values from response
            const data = revision.value;

            // clear extraneous values
            const remove = ['sunrise-date', 'sunrise-time', 'sunset-time', 'sunset-date', 'submit', 'action', 'comment-textarea'];
            remove.forEach(rmv => {
              delete data[rmv];
            });

            // grab the number for the next step in approval workflow
            const approval = revision.approval - 1;

            // if below zero, this content is already approved
            if (approval < 0) {
              const err = {
                message: config.workflows.messages.approved,
                safe: `/${config.content.base}/${req.params.type}`,
                status: 404,
              };

              return next(err);
            }

            // grab the the next step in approval workflow
            const step = steps[approval];

            const time = {
              sunrise: {
                human: utils.time.input(revision.sunrise, revision['sunrise-timezone']),
                unix: moment(revision.sunrise).unix(),
              },
              sunset: {
                human: utils.time.input(revision.sunset, revision['sunset-timezone']),
                unix: revision.sunset ? moment(revision.sunset).unix() : 0,
              },
              now: moment().unix(),
            };

            let scheduled = [];

            // Get live revision's sunset
            return database('live').select('sunset').where({
              id: revision.id,
              language: revision.language,
            }).then(live => {
              if (live.length > 0) {
                time.sunset.live = {
                  human: utils.time.input(live[0].sunset, revision['sunset-timezone']),
                  unix: live[0].sunset ? moment(live[0].sunset).unix() : 0,
                };
              }

              return live;
            }).then(() => {
              return database('schedule').select('revision').where({
                id: revision.id,
                language: revision.language,
                sunrise: revision.sunrise,
              }).then(items => {
                scheduled = items;

                return workflows.model();
              });
            }).then(model => {
              return types.only('workflow', values, model, config).then(merged => {
                return types.form(merged, errors, config).then(form => {
                  res.render('content/approval', {
                    title: `${config.content.actions.approve} :: ${req.params.type}`,
                    form,
                    action: `/${config.content.base}/${req.params.type.toLowerCase()}/${config.content.actions.approve}`,
                    revision,
                    time,
                    scheduled,
                    data,
                    type: ct,
                    config: config.content,
                    step,
                  });
                });
              });
            }).catch(error => {
              next(error);
            });
          });
      });
    });

    /*
     * @name Save - Post to Workflows
     * Save workflow to db
     *
     * @param {object} req - HTTP Request
     * @param {object} res - HTTP Response
     * @param {object} next - Express callback
    */
    app.post(`/${config.content.base}/:type/${config.content.actions.approve}`, (req, res, next) => {
      const revision = _.get(req.session, `form.content.approve[${req.params.type.toLowerCase()}].revision`, '');
      const id = _.get(req.session, `form.content.approve[${req.params.type.toLowerCase()}].id`, '');
      const referrer = _.get(req.session, 'referrer') || req.get('Referrer');
      let working;

      return workflows.model().then(model => {
        return types.only('workflow', {}, model, config).then(merged => {
          // validate audit entry
          const validated = types.form.validate(req.body, merged);

          if (validated === true) {
            // grab the current workflow entries from the database
            return database(`content-type--${req.content.type.id}`).select('*').where('revision', revision).then(rows => {
              const rev = rows[0];
              const audits = workflows.audits(rev, req.content.workflow, req);

              working = _.merge({}, rev, audits);

              return database(`content-type--${req.params.type.toLowerCase()}`)
                .where('revision', '=', parseInt(revision, 10))
                .update(audits);
            }).then(() => {
              const latest = utils.routes.identifier([working], req.content.type)[0];

              if (latest.publishable && latest.approval === 0) {
                return schedule(latest, req.content.type, req.app.get('applications-apps')).then(() => {
                  return latest;
                });
              }

              return latest;
            }).then(() => {
              _.unset(req.session, 'form.content.approve');

              // redirect to piece-of-content
              res.redirect(`/${config.content.base}/${req.content.type.id}/${id}`);
            }).catch(e => {
              const err = {
                message: 'Something went wrong during save',
                error: e.stack,
                safe: `/${config.applications.base}/${req.content.type.id}/${id}`,
                status: 500,
              };

              return next(err);
            });
          }

          _.set(req.session, 'form.content.approve', {
            [req.params.type.toLowerCase()]: {
              errors: validated,
              content: utils.format(req.body),
            },
          });

          return res.redirect(referrer);
        });
      });
    });

    resolve(app);
  });
};

module.exports = routes;