/**
* @module survey-controller
*/
const utils = require( '../lib/utils' );
const TError = require( '../lib/custom-error' ).TranslatedError;
const communicator = require( '../lib/communicator' );
const surveyModel = require( '../models/survey-model' );
const userModel = require( '../models/user-model' );
const config = require( '../models/config-model' ).server;
const express = require( 'express' );
const router = express.Router();
const routerUtils = require( '../lib/router-utils' );
// var debug = require( 'debug' )( 'survey-controller' );
module.exports = app => {
app.use( `${app.get( 'base path' )}/`, router );
};
router.param( 'enketo_id', routerUtils.enketoId );
router.param( 'encrypted_enketo_id_single', routerUtils.encryptedEnketoIdSingle );
router.param( 'encrypted_enketo_id_view', routerUtils.encryptedEnketoIdView );
router.param( 'mod', ( req, rex, next, mod ) => {
if ( mod === 'i' ) {
req.iframe = true;
next();
} else {
req.iframe = false;
next( 'route' );
}
} );
router
//.get( '*', loggedInCheck )
.get( `${config[ 'offline path' ]}/:enketo_id`, offlineWebform )
.get( `${config[ 'offline path' ]}/`, redirect )
.get( '/connection', ( req, res ) => {
res.status = 200;
res.send( `connected ${Math.random()}` );
} )
.get( '/preview', preview )
.get( '/preview/:mod', preview )
.get( '/preview/:enketo_id', preview )
.get( '/preview/:mod/:enketo_id', preview )
.get( '/:enketo_id', webform )
.get( '/:mod/:enketo_id', webform )
.get( '/single/:enketo_id', single )
.get( '/single/:encrypted_enketo_id_single', single )
.get( '/single/:mod/:enketo_id', single )
.get( '/single/:mod/:encrypted_enketo_id_single', single )
.get( '/view/:encrypted_enketo_id_view', view )
.get( '/view/:mod/:encrypted_enketo_id_view', view )
.get( '/edit/:enketo_id', edit )
.get( '/edit/:mod/:enketo_id', edit )
.get( '/xform/:enketo_id', xform )
.get( '/xform/:encrypted_enketo_id_single', xform )
.get( '/xform/:encrypted_enketo_id_view', xform )
.get( /.*\/::[A-z0-9]{4,8}/, redirect );
// TODO: I suspect this check is no longer used and can be removed
//function loggedInCheck( req, res, next ) {
// req.logout = !!userModel.getCredentials( req );
// next();
//}
/**
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
*/
function offlineWebform( req, res, next ) {
if ( !req.app.get( 'offline enabled' ) ) {
const error = new Error( 'Offline functionality has not been enabled for this application.' );
error.status = 405;
next( error );
} else {
req.offlinePath = config[ 'offline path' ];
webform( req, res, next );
}
}
/**
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
*/
function webform( req, res, next ) {
const options = {
offlinePath: req.offlinePath,
iframe: req.iframe,
print: req.query.print === 'true',
desktop: req.query.desktop === 'true'
};
_renderWebform( req, res, next, options );
}
/**
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
*/
function single( req, res, next ) {
const options = {
type: 'single',
iframe: req.iframe
};
if ( req.encryptedEnketoId && req.cookies[ req.encryptedEnketoId ] ) {
res.redirect( `${req.baseUrl}/thanks?taken=${req.cookies[ req.encryptedEnketoId ]}` );
} else {
_renderWebform( req, res, next, options );
}
}
/**
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
*/
function view( req, res, next ) {
const options = {
type: 'view',
iframe: req.iframe,
print: req.query.print === 'true'
};
_renderWebform( req, res, next, options );
}
/**
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
*/
function preview( req, res, next ) {
const options = {
type: 'preview',
iframe: req.iframe || !!req.query.iframe,
notification: utils.pickRandomItemFromArray( config.notifications )
};
_renderWebform( req, res, next, options );
}
/**
* This serves a page that redirects old pre-2.0.0 urls into new urls.
* The reason this on the client-side is to cache the redirect itself which is important
* in case people have bookmarked an offline-capable old-style url and go into the field without Internet.
*
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
*/
function redirect( req, res ) {
res.render( 'surveys/webform-redirect' );
}
/**
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
*/
function edit( req, res, next ) {
const options = {
type: 'edit',
iframe: req.iframe,
};
if ( req.query.instance_id ) {
_renderWebform( req, res, next, options );
} else {
const error = new TError( 'error.invalidediturl' );
error.status = 400;
next( error );
}
}
/**
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
* @param { object } options - Options passed to render
*/
function _renderWebform( req, res, next, options ) {
const deviceId = req.signedCookies[ '__enketo_meta_deviceid' ] || `${req.hostname}:${utils.randomString( 16 )}`,
cookieOptions = {
signed: true,
maxAge: 10 * 365 * 24 * 60 * 60 * 1000
};
res
.cookie( '__enketo_meta_deviceid', deviceId, cookieOptions )
.render( 'surveys/webform', options );
}
/**
* Debugging view that shows underlying XForm
*
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
*/
function xform( req, res, next ) {
return surveyModel.get( req.enketoId )
.then( survey => {
survey.credentials = userModel.getCredentials( req );
return survey;
} )
.then( communicator.getXFormInfo )
.then( communicator.getXForm )
.then( survey => {
res
.set( 'Content-Type', 'text/xml' )
.send( survey.xform );
} )
.catch( next );
}