tellform/app/models/form.server.model.js

511 lines
12 KiB
JavaScript
Raw Normal View History

2015-06-29 22:51:29 +00:00
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
2015-07-28 23:26:32 +00:00
pdfFiller = require('node-pdffiller'),
2015-06-29 22:51:29 +00:00
_ = require('lodash'),
config = require('../../config/config'),
path = require('path'),
2015-10-06 20:14:38 +00:00
mUtilities = require('mongoose-utilities'),
2015-06-30 14:21:53 +00:00
fs = require('fs-extra'),
2015-07-27 18:11:43 +00:00
async = require('async'),
2015-08-04 21:06:16 +00:00
util = require('util');
//Mongoose Models
2015-09-18 16:32:17 +00:00
var FieldSchema = require('./form_field.server.model.js');
var Field = mongoose.model('Field');
2015-09-10 22:06:28 +00:00
var FormSubmissionSchema = require('./form_submission.server.model.js'),
FormSubmission = mongoose.model('FormSubmission', FormSubmissionSchema);
2015-06-29 22:51:29 +00:00
2015-09-18 16:32:17 +00:00
2015-08-18 21:44:36 +00:00
var ButtonSchema = new Schema({
url: {
type: String,
match: [/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/],
},
action: String,
text: String,
bgColor: {
type: String,
match: [/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/],
default: '#5bc0de'
},
color: {
type: String,
match: [/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/],
default: '#ffffff'
}
});
2015-06-29 22:51:29 +00:00
/**
* Form Schema
*/
2015-09-10 22:06:28 +00:00
var FormSchema = new Schema({
2015-06-29 22:51:29 +00:00
title: {
type: String,
trim: true,
2015-07-28 22:29:07 +00:00
required: 'Form Title cannot be blank',
2015-06-29 22:51:29 +00:00
},
2015-07-07 01:21:43 +00:00
language: {
type: String,
enum: ['english', 'french', 'spanish'],
2015-07-13 19:00:10 +00:00
required: 'Form must have a language',
default: 'english'
2015-07-07 01:21:43 +00:00
},
2015-06-29 22:51:29 +00:00
description: {
type: String,
default: '',
},
2015-10-07 01:16:47 +00:00
// form_fields: [Field],
form_fields: {
type: [FieldSchema],
},
2015-06-29 22:51:29 +00:00
2015-06-30 02:14:43 +00:00
submissions: [{
2015-06-29 22:51:29 +00:00
type: Schema.Types.ObjectId,
ref: 'FormSubmission'
}],
admin: {
type: Schema.Types.ObjectId,
ref: 'User',
required: 'Form must have an Admin'
2015-06-29 22:51:29 +00:00
},
pdf: {
type: Schema.Types.Mixed
},
pdfFieldMap: {
type: Schema.Types.Mixed
},
2015-08-07 21:02:44 +00:00
startPage: {
showStart:{
type: Boolean,
default: false,
},
2015-08-18 21:44:36 +00:00
introTitle:{
2015-08-07 21:02:44 +00:00
type: String,
2015-08-18 21:44:36 +00:00
default: 'Welcome to Form'
2015-08-07 21:02:44 +00:00
},
2015-08-18 21:44:36 +00:00
introParagraph:{
type: String,
},
buttons:[ButtonSchema]
2015-08-06 05:52:59 +00:00
},
2015-08-18 21:44:36 +00:00
2015-06-30 11:05:44 +00:00
hideFooter: {
type: Boolean,
2015-07-07 01:21:43 +00:00
default: false,
2015-06-30 11:05:44 +00:00
},
2015-06-29 22:51:29 +00:00
isGenerated: {
type: Boolean,
default: false,
},
2015-07-02 02:49:35 +00:00
isLive: {
type: Boolean,
2015-07-07 01:21:43 +00:00
default: false,
2015-07-02 02:49:35 +00:00
},
2015-06-29 22:51:29 +00:00
autofillPDFs: {
type: Boolean,
default: false,
},
2015-08-07 21:02:44 +00:00
design: {
colors:{
backgroundColor: {
type: String,
2015-10-30 21:40:22 +00:00
match: [/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/],
default: '#fff'
},
questionColor: {
type: String,
2015-10-30 21:40:22 +00:00
match: [/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/],
default: '#333',
},
answerColor: {
type: String,
2015-10-30 21:40:22 +00:00
match: [/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/],
default: '#333',
},
buttonColor: {
type: String,
match: [/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/]
},
},
font: String,
backgroundImage: { type: Schema.Types.Mixed }
2015-09-10 22:06:28 +00:00
},
plugins: {
oscarhost: {
baseUrl: {
type: String,
},
settings: {
lookupField: {
type: Schema.Types.ObjectId,
ref: 'Field'
},
updateType: {
type: String,
enum: ['upsert', 'force_add', 'force_update', 'fetch'],
},
fieldMap: {
type: Schema.Types.Mixed,
2015-10-30 18:40:02 +00:00
},
validUpdateTypes: {
type: [String]
},
validFields : {
type: [String],
default: [
'address',
'city',
'email',
'firstName',
'hin',
'lastName',
'phone',
'postal',
'province',
'sex',
'spokenLanguage',
'title',
'DOB']
2015-09-10 22:06:28 +00:00
}
},
auth: {
user: {
type: String,
},
pass: {
type: String,
}
}
}
2015-07-28 22:29:07 +00:00
}
2015-06-29 22:51:29 +00:00
});
2015-10-06 20:14:38 +00:00
FormSchema.plugin(mUtilities.timestamp, {
createdPath: 'created',
modifiedPath: 'lastModified',
useVirtual: false
});
2015-10-30 18:40:02 +00:00
FormSchema.pre('save', function (next) {
var validUpdateTypes= mongoose.model('Form').schema.path('plugins.oscarhost.settings.updateType').enumValues;
this.plugins.oscarhost.settings.validUpdateTypes = validUpdateTypes;
2015-10-06 20:14:38 +00:00
2015-10-30 18:40:02 +00:00
this.plugins.oscarhost.settings.validFields = [
'address',
'city',
'email',
'firstName',
'hin',
'lastName',
'phone',
'postal',
'province',
'sex',
'spokenLanguage',
'title',
'DOB'];
2015-10-30 20:14:48 +00:00
next();
2015-10-30 18:40:02 +00:00
});
2015-07-03 00:49:23 +00:00
//Delete template PDF of current Form
2015-09-10 22:06:28 +00:00
FormSchema.pre('remove', function (next) {
if(this.pdf && process.env.NODE_ENV === 'development'){
2015-07-03 00:49:23 +00:00
//Delete template form
fs.unlink(this.pdf.path, function(err){
if (err) throw err;
console.log('successfully deleted', this.pdf.path);
});
}
});
2015-07-28 22:29:07 +00:00
var _original;
2015-07-03 00:49:23 +00:00
2015-07-27 18:11:43 +00:00
//Set _original
2015-09-10 22:06:28 +00:00
FormSchema.pre('save', function (next) {
2015-09-18 16:32:17 +00:00
this.constructor
2015-07-28 22:29:07 +00:00
.findOne({_id: this._id}).exec(function(err, original){
if(err) {
console.log(err);
next(err);
} else {
2015-07-27 18:11:43 +00:00
_original = original;
2015-09-18 16:32:17 +00:00
//console.log('_original');
2015-07-28 22:29:07 +00:00
// console.log(_original);
2015-07-27 18:11:43 +00:00
next();
}
2015-07-28 22:29:07 +00:00
});
2015-07-27 18:11:43 +00:00
});
function getDeletedIndexes(needle, haystack){
var deletedIndexes = [];
if(haystack.length > 0){
for(var i = 0; i < needle.length; i++){
2015-07-27 18:11:43 +00:00
if(haystack.indexOf(needle[i]) === -1){
deletedIndexes.push(i);
}
}
}
return deletedIndexes;
}
2015-07-02 03:50:57 +00:00
//Move PDF to permanent location after new template is uploaded
2015-09-10 22:06:28 +00:00
FormSchema.pre('save', function (next) {
2015-06-29 22:51:29 +00:00
if(this.pdf){
2015-07-03 00:49:23 +00:00
var that = this;
async.series([
function(callback){
2015-07-27 18:11:43 +00:00
if(that.isModified('pdf') && that.pdf.path){
2015-07-03 00:49:23 +00:00
var new_filename = that.title.replace(/ /g,'')+'_template.pdf';
var newDestination = path.join(config.pdfUploadPath, that.admin.username.replace(/ /g,''), that.title.replace(/ /g,'')),
stat = null;
try {
stat = fs.statSync(newDestination);
} catch (err) {
fs.mkdirSync(newDestination);
}
if (stat && !stat.isDirectory()) {
2015-07-27 18:11:43 +00:00
return callback( new Error('Directory cannot be created because an inode of a different type exists at "' + config.pdfUploadPath + '"'), null);
2015-07-03 00:49:23 +00:00
}
2015-07-27 18:11:43 +00:00
var old_path = that.pdf.path;
fs.move(old_path, path.join(newDestination, new_filename), {clobber: true}, function (err) {
2015-07-03 00:49:23 +00:00
if (err) {
console.error(err);
2015-07-27 18:11:43 +00:00
callback( new Error(err.message), 'task1');
}else {
that.pdf.path = path.join(newDestination, new_filename);
that.pdf.name = new_filename;
2015-07-03 00:49:23 +00:00
2015-07-27 18:11:43 +00:00
callback(null,'task1');
}
2015-07-03 00:49:23 +00:00
});
2015-07-27 18:11:43 +00:00
}else {
callback(null,'task1');
2015-07-03 00:49:23 +00:00
}
2015-07-27 18:11:43 +00:00
2015-07-03 00:49:23 +00:00
},
function(callback){
if(that.isGenerated){
that.pdf.path = path.join(config.pdfUploadPath, that.admin.username.replace(/ /g,''), that.title.replace(/ /g,''), that.title.replace(/ /g,'')+'_template.pdf');
that.pdf.name = that.title.replace(/ /g,'')+'_template.pdf';
var _typeConvMap = {
'Multiline': 'textarea',
'Text': 'textfield',
'Button': 'checkbox',
'Choice': 'radio',
'Password': 'password',
'FileSelect': 'filefield',
'Radio': 'radio'
};
2015-07-27 18:11:43 +00:00
console.log('autogenerating form');
// console.log(that.pdf.path);
2015-07-03 00:49:23 +00:00
pdfFiller.generateFieldJson(that.pdf.path, function(err, _form_fields){
if(err){
callback( new Error(err.message), null);
}else if(!_form_fields.length || _form_fields === undefined || _form_fields === null){
callback( new Error('Generated formfields is empty'), null);
}
//Map PDF field names to FormField field names
for(var i = 0; i < _form_fields.length; i++){
var field = _form_fields[i];
//Convert types from FDF to 'FormField' types
if(_typeConvMap[ field.fieldType+'' ]){
field.fieldType = _typeConvMap[ field.fieldType+'' ];
2015-07-03 00:49:23 +00:00
}
2015-10-06 20:14:38 +00:00
// field = new Field(field);
field.required = false;
_form_fields[i] = field;
}
// console.log('NEW FORM_FIELDS: ');
// console.log(_form_fields);
2015-07-03 00:49:23 +00:00
2015-07-27 18:11:43 +00:00
that.form_fields = that.form_fields.concat(_form_fields);
// console.log('\n\nOLD FORM_FIELDS: ');
// console.log(that.form_fields);
2015-07-27 18:11:43 +00:00
that.isGenerated = false;
callback(null, 'task2');
});
}else{
callback(null, 'task2');
}
2015-07-03 00:49:23 +00:00
}
], function(err, results) {
if(err){
next(new Error({
message: err.message
}));
}
console.log('ending form save');
next();
2015-07-02 02:49:35 +00:00
});
2015-07-27 18:11:43 +00:00
}else if(_original){
if(_original.pdf){
fs.remove(_original.pdf.path, function (err) {
if(err) next(err);
console.log('file at '+_original.pdf.path+' successfully deleted');
next();
});
}
2015-07-02 02:49:35 +00:00
}
2015-07-28 22:29:07 +00:00
next();
2015-07-02 02:49:35 +00:00
});
2015-09-10 22:06:28 +00:00
FormSchema.pre('save', function (next) {
2015-07-28 22:29:07 +00:00
// var _original = this._original;
// console.log('_original\n------------');
2015-07-27 18:11:43 +00:00
// console.log(_original);
2015-10-06 20:14:38 +00:00
//console.log('field has been deleted: ');
//console.log(this.isModified('form_fields') && !!this.form_fields && !!_original);
2015-07-27 18:36:44 +00:00
2015-07-28 22:29:07 +00:00
if(this.isModified('form_fields') && this.form_fields.length >= 0 && _original){
2015-07-27 18:11:43 +00:00
var old_form_fields = _original.form_fields,
new_ids = _.map(_.pluck(this.form_fields, '_id'), function(id){ return ''+id;}),
old_ids = _.map(_.pluck(old_form_fields, '_id'), function(id){ return ''+id;}),
deletedIds = getDeletedIndexes(old_ids, new_ids),
that = this;
2015-10-06 20:14:38 +00:00
console.log('deletedId Indexes\n--------');
console.log(deletedIds);
console.log('old_ids\n--------');
console.log(old_ids);
console.log('new_ids\n--------');
console.log(new_ids);
2015-07-27 18:11:43 +00:00
//Preserve fields that have at least one submission
if( deletedIds.length > 0 ){
var modifiedSubmissions = [];
async.forEachOfSeries(deletedIds,
function (deletedIdIndex, key, callback) {
var deleted_id = old_ids[deletedIdIndex];
//Search for submissions with deleted form_field
// if(submissions.length){
// submissionsWithDeletedField = _.select(form.submissions, function(submission){
// var field = _(submission.form_fields).filter(function(field) { return field._id === deleted_id; })
// return !!field;
// });
// //Push old form_field to start of array
// if(submissionsWithDeletedField.length){
// that.form_fields.unshift(old_form_fields[deletedIdIndex]);
// modifiedSubmissions.push.apply(modifiedSubmissions, submissionsWithDeletedField);
// console.log(modifiedSubmissions);
// }
// callback(null, modifiedSubmissions);
// } else{
2015-10-06 20:14:38 +00:00
//Find FormSubmissions that contain field with _id equal to 'deleted_id'
2015-07-27 18:11:43 +00:00
FormSubmission.
2015-10-06 20:14:38 +00:00
find({ form: that._id, admin: that.admin, form_fields: {$elemMatch: {_id: deleted_id} } }).
2015-07-27 18:11:43 +00:00
exec(function(err, submissions){
if(err){
console.error(err);
return callback(err);
}
2015-10-06 20:14:38 +00:00
//Delete field if there are no submission(s) found
if(submissions.length) {
console.log('adding submissions');
console.log(submissions);
//Add submissions
modifiedSubmissions.push.apply(modifiedSubmissions, submissions);
}
callback(null);
});
2015-07-27 18:11:43 +00:00
// }
},
function (err) {
if(err){
console.error(err.message);
next(err);
}
// console.log('modifiedSubmissions\n---------\n\n');
// console.log(modifiedSubmissions);
2015-10-06 20:14:38 +00:00
//Iterate through all submissions with modified form_fields
2015-07-27 18:11:43 +00:00
async.forEachOfSeries(modifiedSubmissions, function (submission, key, callback) {
2015-10-06 20:14:38 +00:00
//Iterate through ids of deleted fields
2015-07-27 18:11:43 +00:00
for(var i = 0; i < deletedIds.length; i++){
2015-10-06 20:14:38 +00:00
//Get index of deleted field
2015-07-27 18:11:43 +00:00
var index = _.findIndex(submission.form_fields, function(field) {
var tmp_id = field._id+'';
return tmp_id === old_ids[ deletedIds[i] ];
});
2015-10-06 20:14:38 +00:00
var deletedField = submission.form_fields[index];
2015-07-27 18:11:43 +00:00
2015-10-06 20:14:38 +00:00
//Hide field if it exists
if(deletedField){
console.log('deletedField\n-------\n\n');
console.log(deletedField);
2015-07-27 18:11:43 +00:00
//Delete old form_field
submission.form_fields.splice(index, 1);
2015-10-06 20:14:38 +00:00
deletedField.deletePreserved = true;
2015-07-27 18:11:43 +00:00
2015-10-06 20:14:38 +00:00
//Move deleted form_field to start
submission.form_fields.unshift(deletedField);
that.form_fields.unshift(deletedField);
2015-07-27 18:11:43 +00:00
// console.log('form.form_fields\n--------\n\n');
// console.log(that.form_fields);
}
}
submission.save(function (err) {
if(err) callback(err);
else callback(null);
});
}, function (err) {
if(err){
console.error(err.message);
next(err);
}
// console.log('form.form_fields\n--------\n\n');
// console.log(that.form_fields);
next();
});
}
);
}else {
next();
}
}else {
next();
}
});
2015-06-29 22:51:29 +00:00
2015-09-10 22:06:28 +00:00
mongoose.model('Form', FormSchema);
2015-06-29 22:51:29 +00:00