added backend tests

This commit is contained in:
David Baldwynn 2015-08-07 14:02:44 -07:00
parent 81cab5515e
commit 7885223a68
42 changed files with 1328 additions and 371 deletions

View file

@ -10,7 +10,7 @@ var client = new raven.Client(config.DSN);
*/ */
exports.index = function(req, res) { exports.index = function(req, res) {
// next( throw new Error('Hello, world!')); // next( throw new Error('Hello, world!'));
client.captureMessage('Rendering index.html'); // client.captureMessage('Rendering index.html');
res.render('index', { res.render('index', {
user: req.user || null, user: req.user || null,
request: req request: req

View file

@ -19,11 +19,12 @@ var mongoose = require('mongoose'),
*/ */
exports.uploadPDF = function(req, res, next) { exports.uploadPDF = function(req, res, next) {
console.log('inside uploadPDF'); // console.log('inside uploadPDF');
// console.log(req.files.file); // console.log(req.files.file);
// console.log('\n\nProperty Descriptor\n-----------'); // console.log('\n\nProperty Descriptor\n-----------');
// console.log(Object.getOwnPropertyDescriptor(req.files.file, 'path')); // console.log(Object.getOwnPropertyDescriptor(req.files.file, 'path'));
if(req.files){ if(req.files){
var pdfFile = req.files.file; var pdfFile = req.files.file;
var _user = req.user; var _user = req.user;
@ -73,6 +74,7 @@ exports.uploadPDF = function(req, res, next) {
*/ */
exports.deleteSubmissions = function(req, res) { exports.deleteSubmissions = function(req, res) {
console.log(req.body); console.log(req.body);
var submission_id_list = req.body.deleted_submissions, var submission_id_list = req.body.deleted_submissions,
form = req.form; form = req.form;
@ -92,56 +94,46 @@ exports.deleteSubmissions = function(req, res) {
*/ */
exports.createSubmission = function(req, res) { exports.createSubmission = function(req, res) {
var submission, var form = req.form;
form = req.form, // console.log('in createSubmission()');
fdfData, // console.log(req.body);
fdfTemplate,
that = this;
console.log(req.body.percentageComplete);
submission = new FormSubmission({ var submission = new FormSubmission({
admin: req.user, admin: req.form.admin._id,
form: req.form._id,
title: req.form.title,
form_fields: req.body.form_fields, form_fields: req.body.form_fields,
timeElapsed: req.body.timeElapsed, timeElapsed: req.body.timeElapsed,
percentageComplete: req.body.percentageComplete percentageComplete: req.body.percentageComplete
}); });
submission.form = form; if(form.pdf) submission.pdf = form.pdf;
submission.pdf = form.pdf;
submission.title = form.title;
if(req.headers['x-forwarded-for'] || req.connection.remoteAddress){ if(req.headers['x-forwarded-for'] || req.connection.remoteAddress){
var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
console.log('ip address of client is: '+ip); // console.log('ip address of client is: '+ip);
// if(ip) submission.ipAddr = ip;
} }
// submission.ipAddr = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
if(form.autofillPDFs){ if(form.autofillPDFs){
if (form.isGenerated){ try {
fdfTemplate = form.generateFDFTemplate(); submission.fdfData = pdfFiller.convFieldJson2FDF(submission.form_fields);
} else { } catch(err){
try { res.status(400).send({
fdfTemplate = pdfFiller.mapForm2PDF(form.generateFDFTemplate(), form.pdfFieldMap); message: errorHandler.getErrorMessage(err)
} catch(err){ });
res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
}
} }
fdfData = pdfFiller.convFieldJson2FDF(submission.form_fields);
submission.fdfData = fdfData;
}else{ }else{
submission.fdfData = undefined; submission.fdfData = null;
} }
submission.save(function(err){ submission.save(function(err, submission){
// console.log('in submissions.save()\n submission: '+JSON.stringify(submission) );
if(err){ if(err){
res.status(400).send({ res.status(400).send({
message: errorHandler.getErrorMessage(err) message: errorHandler.getErrorMessage(err)
}); });
} }
// console.log(results);
// console.log(that.form_fields);
res.status(200).send('Form submission successfully saved'); res.status(200).send('Form submission successfully saved');
}); });
}; };
@ -152,6 +144,7 @@ exports.createSubmission = function(req, res) {
exports.listSubmissions = function(req, res) { exports.listSubmissions = function(req, res) {
var _form = req.form; var _form = req.form;
var _user = req.user; var _user = req.user;
// console.log('listSubmissions');
// console.log(_form); // console.log(_form);
// if(_form.submissions.length){ // if(_form.submissions.length){
@ -186,8 +179,10 @@ exports.listSubmissions = function(req, res) {
exports.create = function(req, res) { exports.create = function(req, res) {
var form = new Form(req.body.form); var form = new Form(req.body.form);
form.admin = req.user; form.admin = req.user._id;
// console.log('Create a new form');
// console.log(form); // console.log(form);
// console.log(req.body.form)
// console.log(req.user); // console.log(req.user);
form.save(function(err) { form.save(function(err) {
@ -214,15 +209,13 @@ exports.read = function(req, res) {
* Update a form * Update a form
*/ */
exports.update = function(req, res) { exports.update = function(req, res) {
console.log('in form.update()'); // console.log('in form.update()');
// console.log(req.body.form.form_fields); // console.log(req.body.form.form_fields);
var form = req.form; var form = req.form;
delete req.body.form.__v; delete req.body.form.__v;
delete req.body.form._id; delete req.body.form._id;
delete req.body.form.created;
delete req.body.form.lastModified;
delete req.body.form.admin; delete req.body.form.admin;
// console.log(form.admin); // console.log(form.admin);
@ -241,7 +234,7 @@ exports.update = function(req, res) {
message: errorHandler.getErrorMessage(err) message: errorHandler.getErrorMessage(err)
}); });
} else { } else {
console.log('updated form'); // console.log('updated form');
res.json(form); res.json(form);
} }
}); });
@ -252,15 +245,16 @@ exports.update = function(req, res) {
*/ */
exports.delete = function(req, res) { exports.delete = function(req, res) {
var form = req.form; var form = req.form;
console.log('deleting form'); // console.log('deleting form');
Form.remove({_id: form._id}, function(err) { Form.remove({_id: form._id}, function(err) {
if (err) { if (err) {
res.status(500).send({ res.status(400).send({
message: errorHandler.getErrorMessage(err) message: errorHandler.getErrorMessage(err)
}); });
} else { } else {
console.log('Form successfully deleted'); // console.log('Form successfully deleted');
res.status(200).send('Form successfully deleted'); // res.status(200).send('Form successfully deleted');
res.json(form);
} }
}); });
}; };
@ -305,16 +299,12 @@ exports.formByID = function(req, res, next, id) {
}); });
} }
else { else {
if(!form.admin.username){
form.admin = req.user;
}
// console.log(form.admin); // console.log(form.admin);
//Remove sensitive information from User object //Remove sensitive information from User object
form.admin.password = null; form.admin.password = undefined;
form.admin.created = null; form.admin.salt = undefined;
form.admin.salt = null; form.provider = undefined;
form.provider = null;
req.form = form; req.form = form;
next(); next();
@ -330,7 +320,7 @@ exports.hasAuthorization = function(req, res, next) {
var form = req.form; var form = req.form;
if (req.form.admin.id !== req.user.id && req.user.roles.indexOf('admin') === -1) { if (req.form.admin.id !== req.user.id && req.user.roles.indexOf('admin') === -1) {
res.status(403).send({ res.status(403).send({
message: 'User '+req.user.username+' is not authorized' message: 'User '+req.user.username+' is not authorized to edit Form: '+form.title
}); });
} }
next(); next();

View file

@ -17,58 +17,32 @@ var _ = require('lodash'),
var smtpTransport = nodemailer.createTransport(config.mailer.options); var smtpTransport = nodemailer.createTransport(config.mailer.options);
// NEV configuration ===================== // // NEV configuration =====================
nev.configure({ // nev.configure({
persistentUserModel: User, // persistentUserModel: User,
expirationTime: 1800, // 30 minutes // tempUserCollection: config.tempUserCollection,
// expirationTime: 1800, // 30 minutes
verificationURL: config.baseUrl+'/#!/verify/${URL}', // verificationURL: config.baseUrl+'/#!/verify/${URL}',
transportOptions: config.mailer.options, // transportOptions: config.mailer.options,
verifyMailOptions: { // verifyMailOptions: {
from: config.mailer.from, // from: config.mailer.from,
subject: 'Confirm your account', // subject: 'Confirm your account',
html: '<p>Please verify your account by clicking <a href="${URL}">this link</a>. If you are unable to do so, copy and ' + // html: '<p>Please verify your account by clicking <a href="${URL}">this link</a>. If you are unable to do so, copy and ' +
'paste the following link into your browser:</p><p>${URL}</p>', // 'paste the following link into your browser:</p><p>${URL}</p>',
text: 'Please verify your account by clicking the following link, or by copying and pasting it into your browser: ${URL}' // text: 'Please verify your account by clicking the following link, or by copying and pasting it into your browser: ${URL}'
}, // },
confirmMailOptions: { // confirmMailOptions: {
from: config.mailer.from, // from: config.mailer.from,
subject: 'Successfully verified!', // subject: 'Successfully verified!',
html: '<p>Your account has been successfully verified.</p>', // html: '<p>Your account has been successfully verified.</p>',
text: 'Your account has been successfully verified.' // text: 'Your account has been successfully verified.'
}, // },
}); // });
nev.generateTempUserModel(User); // nev.generateTempUserModel(User);
/**
* Signup
*/
exports.signup = function(req, res) {
// For security measurement we remove the roles from the req.body object
delete req.body.roles;
// Init Variables
var user = new User(req.body);
// Add missing user fields
user.provider = 'local';
user.username = user.email;
// user.displayName = user.firstName + ' ' + user.lastName;
// Then save the temporary user
nev.createTempUser(user, function(newTempUser) {
// new user created
if (newTempUser) {
nev.registerTempUser(newTempUser);
res.status(200).send('An email has been sent to you. Please check it to verify your account.');
} else {
res.status(400).send('Error: Temp user could NOT be created!');
}
});
};
exports.validateVerificationToken = function(req, res, next){ exports.validateVerificationToken = function(req, res, next){
@ -93,6 +67,33 @@ exports.resendVerificationEmail = function(req, res, next){
}); });
}; };
/**
* Signup
*/
exports.signup = function(req, res) {
// For security measurement we remove the roles from the req.body object
delete req.body.roles;
// Init Variables
var user = new User(req.body);
// Add missing user fields
user.provider = 'local';
user.username = user.email;
// Then save the temporary user
nev.createTempUser(user, function(newTempUser) {
// new user created
if (newTempUser) {
nev.registerTempUser(newTempUser);
res.status(200).send('An email has been sent to you. Please check it to verify your account.');
} else {
res.status(400).send('Error: Temp user could NOT be created!');
}
});
};
/** /**
* Signin after passport authentication * Signin after passport authentication
*/ */
@ -104,6 +105,7 @@ exports.signin = function(req, res, next) {
// Remove sensitive data before login // Remove sensitive data before login
user.password = undefined; user.password = undefined;
user.salt = undefined; user.salt = undefined;
user.provider = undefined;
req.login(user, function(err) { req.login(user, function(err) {
if (err) { if (err) {

View file

@ -57,8 +57,6 @@ exports.me = function(req, res) {
delete _user.salt; delete _user.salt;
delete _user.provider; delete _user.provider;
delete _user.__v; delete _user.__v;
delete _user.created;
res.json(req.user || null); res.json(req.user || null);
}; };

View file

@ -1,24 +0,0 @@
// var mongoose = require('mongoose'),
// Schema = mongoose.Schema,
// shortid = require('shortid');
// var ObjectId = Schema.ObjectId;
// var BaseSchema = function() {
// Schema.apply(this, arguments);
// this.add({
// created: {
// type: Date,
// default: Date.now
// },
// lastModified: {
// type: Date,
// },
// _id: {
// type: String,
// unique: true,
// 'default': shortid.generate
// },
// });
// }

View file

@ -68,9 +68,17 @@ var FormSchema = new Schema({
type: Schema.Types.Mixed type: Schema.Types.Mixed
}, },
showStart: { startPage: {
type: Boolean, showStart:{
default: false, type: Boolean,
default: false,
},
introText:{
type: String,
},
buttonText:{
type: String
}
}, },
hideFooter: { hideFooter: {
type: Boolean, type: Boolean,
@ -88,17 +96,13 @@ var FormSchema = new Schema({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
saveCount: {
type: Number,
default: 0,
},
design: { design: {
colors:{ colors:{
backgroundColor: String, backgroundColor: String,
questionColor: String, questionColor: String,
answerColor: String, answerColor: String,
buttonColor: String, buttonColor: String,
}, },
font: String, font: String,
backgroundImage: { type: Schema.Types.Mixed } backgroundImage: { type: Schema.Types.Mixed }
@ -138,8 +142,6 @@ var _original;
//Set _original //Set _original
FormSchema.pre('save', function (next) { FormSchema.pre('save', function (next) {
this.saveCount = this.saveCount++;
console.log('saveCount: '+this.saveCount);
// console.log(this.constructor.model); // console.log(this.constructor.model);
// console.log(FormModel); // console.log(FormModel);
this.constructor // ≈ mongoose.model('…', FieldSchema).findById this.constructor // ≈ mongoose.model('…', FieldSchema).findById
@ -148,7 +150,6 @@ FormSchema.pre('save', function (next) {
console.log(err); console.log(err);
next(err); next(err);
} else { } else {
_original = original; _original = original;
// console.log('_original'); // console.log('_original');
// console.log(_original); // console.log(_original);
@ -309,7 +310,7 @@ FormSchema.pre('save', function (next) {
deletedIds = getDeletedIndexes(old_ids, new_ids), deletedIds = getDeletedIndexes(old_ids, new_ids),
that = this; that = this;
console.log('deletedId Indexes\n--------'); // console.log('deletedId Indexes\n--------');
// console.log(deletedIds); // console.log(deletedIds);
// console.log('old_ids\n--------'); // console.log('old_ids\n--------');
// console.log(old_ids); // console.log(old_ids);
@ -372,7 +373,7 @@ FormSchema.pre('save', function (next) {
// console.log('modifiedSubmissions\n---------\n\n'); // console.log('modifiedSubmissions\n---------\n\n');
// console.log(modifiedSubmissions); // console.log(modifiedSubmissions);
console.log('preserved deleted fields'); // console.log('preserved deleted fields');
// console.log(submissions); // console.log(submissions);
async.forEachOfSeries(modifiedSubmissions, function (submission, key, callback) { async.forEachOfSeries(modifiedSubmissions, function (submission, key, callback) {
@ -428,21 +429,21 @@ FormSchema.pre('save', function (next) {
} }
}); });
FormSchema.methods.generateFDFTemplate = function() { // FormSchema.methods.generateFDFTemplate = function() {
var _keys = _.pluck(this.form_fields, 'title'), // var _keys = _.pluck(this.form_fields, 'title'),
_values = _.pluck(this.form_fields, 'fieldValue'); // _values = _.pluck(this.form_fields, 'fieldValue');
_values.forEach(function(val){ // _values.forEach(function(val){
if(val === true){ // if(val === true){
val = 'Yes'; // val = 'Yes';
}else if(val === false) { // }else if(val === false) {
val = 'Off'; // val = 'Off';
} // }
}); // });
var jsonObj = _.zipObject(_keys, _values); // var jsonObj = _.zipObject(_keys, _values);
return jsonObj; // return jsonObj;
}; // };
mongoose.model('Form', FormSchema); mongoose.model('Form', FormSchema);

View file

@ -91,7 +91,7 @@ FormSubmissionSchema.pre('save', function (next) {
if(this.pdf && this.pdf.path){ if(this.pdf && this.pdf.path){
console.log(this.pdf); // console.log(this.pdf);
dest_filename = that.title.replace(/ /g,'')+'_submission_'+Date.now()+'.pdf'; dest_filename = that.title.replace(/ /g,'')+'_submission_'+Date.now()+'.pdf';
var __path = this.pdf.path.split('/').slice(0,this.pdf.path.split('/').length-1).join('/'); var __path = this.pdf.path.split('/').slice(0,this.pdf.path.split('/').length-1).join('/');
dest_path = path.join(__path, dest_filename); dest_path = path.join(__path, dest_filename);
@ -99,8 +99,8 @@ FormSubmissionSchema.pre('save', function (next) {
that.pdfFilePath = dest_path; that.pdfFilePath = dest_path;
pdfFiller.fillForm(that.pdf.path, dest_path, that.fdfData, function(err){ pdfFiller.fillForm(that.pdf.path, dest_path, that.fdfData, function(err){
console.log('fdfData: \n'); // console.log('fdfData: \n');
console.log(that.fdfData); // console.log(that.fdfData);
// console.log('_form.pdf.path: '+_form.pdf.path); // console.log('_form.pdf.path: '+_form.pdf.path);
// console.log('dest_path: '+dest_path); // console.log('dest_path: '+dest_path);

View file

@ -43,7 +43,7 @@ var UserSchema = new Schema({
email: { email: {
type: String, type: String,
trim: true, trim: true,
unique: 'Account already exists with email', unique: 'Account already exists with this email',
required: 'Please enter your email', required: 'Please enter your email',
validate: [validateLocalStrategyProperty, 'Please fill in your email'], validate: [validateLocalStrategyProperty, 'Please fill in your email'],
match: [/.+\@.+\..+/, 'Please fill a valid email address'] match: [/.+\@.+\..+/, 'Please fill a valid email address']
@ -51,7 +51,7 @@ var UserSchema = new Schema({
username: { username: {
type: String, type: String,
unique: true, unique: true,
required: true, required: false,
trim: true trim: true
}, },
password: { password: {
@ -106,10 +106,8 @@ UserSchema.virtual('displayName').get(function () {
//Create folder for user's pdfs //Create folder for user's pdfs
UserSchema.pre('save', function (next) { UserSchema.pre('save', function (next) {
this.username = this.email;
if(process.env.NODE_ENV === 'development'){ if(process.env.NODE_ENV === 'development'){
if(!this.username || this.username !== this.email){
this.username = this.email;
}
var newDestination = path.join(config.pdfUploadPath, this.username.replace(/ /g,'')), var newDestination = path.join(config.pdfUploadPath, this.username.replace(/ /g,'')),
stat = null; stat = null;
@ -184,7 +182,6 @@ UserSchema.statics.findUniqueUsername = function(username, suffix, callback) {
* Function to check if user has Admin priviledges * Function to check if user has Admin priviledges
*/ */
UserSchema.methods.isAdmin = function() { UserSchema.methods.isAdmin = function() {
if(this.roles.indexOf('admin') !== -1){ if(this.roles.indexOf('admin') !== -1){
return true; return true;
} }

View file

@ -29,32 +29,32 @@ module.exports = function(app) {
app.route('/auth/signin').post(users.signin); app.route('/auth/signin').post(users.signin);
app.route('/auth/signout').get(users.signout); app.route('/auth/signout').get(users.signout);
// Setting the facebook oauth routes // // Setting the facebook oauth routes
app.route('/auth/facebook').get(passport.authenticate('facebook', { // app.route('/auth/facebook').get(passport.authenticate('facebook', {
scope: ['email'] // scope: ['email']
})); // }));
app.route('/auth/facebook/callback').get(users.oauthCallback('facebook')); // app.route('/auth/facebook/callback').get(users.oauthCallback('facebook'));
// Setting the twitter oauth routes // // Setting the twitter oauth routes
app.route('/auth/twitter').get(passport.authenticate('twitter')); // app.route('/auth/twitter').get(passport.authenticate('twitter'));
app.route('/auth/twitter/callback').get(users.oauthCallback('twitter')); // app.route('/auth/twitter/callback').get(users.oauthCallback('twitter'));
// Setting the google oauth routes // // Setting the google oauth routes
app.route('/auth/google').get(passport.authenticate('google', { // app.route('/auth/google').get(passport.authenticate('google', {
scope: [ // scope: [
'https://www.googleapis.com/auth/userinfo.profile', // 'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email' // 'https://www.googleapis.com/auth/userinfo.email'
] // ]
})); // }));
app.route('/auth/google/callback').get(users.oauthCallback('google')); // app.route('/auth/google/callback').get(users.oauthCallback('google'));
// Setting the linkedin oauth routes // // Setting the linkedin oauth routes
app.route('/auth/linkedin').get(passport.authenticate('linkedin')); // app.route('/auth/linkedin').get(passport.authenticate('linkedin'));
app.route('/auth/linkedin/callback').get(users.oauthCallback('linkedin')); // app.route('/auth/linkedin/callback').get(users.oauthCallback('linkedin'));
// Setting the github oauth routes // // Setting the github oauth routes
app.route('/auth/github').get(passport.authenticate('github')); // app.route('/auth/github').get(passport.authenticate('github'));
app.route('/auth/github/callback').get(users.oauthCallback('github')); // app.route('/auth/github/callback').get(users.oauthCallback('github'));
// Finish by binding the user middleware // Finish by binding the user middleware
app.param('userId', users.userByID); app.param('userId', users.userByID);

View file

@ -122,28 +122,28 @@ describe('Form Model Unit Tests:', function() {
}); });
}); });
it('should preserve deleted form_fields that have submissions without any problems', function(done) { // it('should preserve deleted form_fields that have submissions without any problems', function(done) {
old_fields = myForm.toObject().form_fields; // old_fields = myForm.toObject().form_fields;
// console.log(old_fields); // // console.log(old_fields);
// var expected_fields = old_fields.slice(1,3).concat(old_fields.slice(0,1)); // // var expected_fields = old_fields.slice(1,3).concat(old_fields.slice(0,1));
myForm.form_fields = new_form_fields_del; // myForm.form_fields = new_form_fields_del;
myForm.save(function(err, _form) { // myForm.save(function(err, _form) {
should.not.exist(err); // should.not.exist(err);
should.exist(_form); // should.exist(_form);
// var actual_fields = _.map(_form.toObject().form_fields, function(o){ _.omit(o, '_id')}); // // var actual_fields = _.map(_form.toObject().form_fields, function(o){ _.omit(o, '_id')});
// old_fields = _.map(old_fields, function(o){ _.omit(o, '_id')}); // // old_fields = _.map(old_fields, function(o){ _.omit(o, '_id')});
// console.log(old_fields); // // console.log(old_fields);
should.deepEqual(JSON.stringify(_form.toObject().form_fields), JSON.stringify(old_fields), 'old form_fields not equal to newly saved form_fields'); // should.deepEqual(JSON.stringify(_form.toObject().form_fields), JSON.stringify(old_fields), 'old form_fields not equal to newly saved form_fields');
done(); // done();
}); // });
}); // });
// it('should delete \'preserved\' form_fields whose submissions have been removed without any problems', function(done) { // it('should delete \'preserved\' form_fields whose submissions have been removed without any problems', function(done) {
@ -161,25 +161,25 @@ describe('Form Model Unit Tests:', function() {
// }); // });
}); });
describe('Method generateFDFTemplate', function() { // describe('Method generateFDFTemplate', function() {
var FormFDF; // var FormFDF;
before(function(done){ // before(function(done){
return myForm.save(function(err, form){ // return myForm.save(function(err, form){
FormFDF = { // FormFDF = {
'First Name': '', // 'First Name': '',
'nascar': '', // 'nascar': '',
'hockey': '' // 'hockey': ''
}; // };
done(); // done();
}); // });
}); // });
it('should be able to generate a FDF template without any problems', function() { // it('should be able to generate a FDF template without any problems', function() {
var fdfTemplate = myForm.generateFDFTemplate(); // var fdfTemplate = myForm.generateFDFTemplate();
(fdfTemplate).should.be.eql(FormFDF); // (fdfTemplate).should.be.eql(FormFDF);
}); // });
}); // });
afterEach(function(done) { afterEach(function(done) {
Form.remove({}, function() { Form.remove({}, function() {

View file

@ -0,0 +1,496 @@
'use strict';
var should = require('should'),
_ = require('lodash'),
app = require('../../server'),
request = require('supertest'),
Session = require('supertest-session')({
app: app
}),
mongoose = require('mongoose'),
User = mongoose.model('User'),
Form = mongoose.model('Form'),
FormSubmission = mongoose.model('FormSubmission'),
agent = request.agent(app);
/**
* Globals
*/
var credentials, user, _Form;
/**
* Form routes tests
*/
describe('Form CRUD tests', function() {
beforeEach(function(done) {
// Create user credentials
credentials = {
username: 'test@test.com',
password: 'password'
};
// Create a new user
user = new User({
firstName: 'Full',
lastName: 'Name',
email: 'test@test.com',
username: credentials.username,
password: credentials.password,
provider: 'local'
});
// Save a user to the test db and create new Form
user.save(function(err) {
if(err) done(err);
_Form = {
title: 'Form Title',
language: 'english',
admin: user._id,
form_fields: [
{'fieldType':'textfield', 'title':'First Name', 'fieldValue': ''},
{'fieldType':'checkbox', 'title':'nascar', 'fieldValue': ''},
{'fieldType':'checkbox', 'title':'hockey', 'fieldValue': ''}
]
};
done();
});
});
it('should be able to save a Form if logged in', function(done) {
agent.post('/auth/signin')
.send(credentials)
.expect('Content-Type', /json/)
.expect(200)
.end(function(signinErr, signinRes) {
// Handle signin error
if (signinErr) done(signinErr);
var user = signinRes.body;
var userId = user._id;
// Save a new Form
agent.post('/forms')
.send({form: _Form})
.expect('Content-Type', /json/)
.expect(200)
.end(function(FormSaveErr, FormSaveRes) {
// Handle Form save error
if (FormSaveErr) done(FormSaveErr);
// Get a list of Forms
agent.get('/forms')
.expect('Content-Type', /json/)
.expect(200)
.end(function(FormsGetErr, FormsGetRes) {
// Handle Form save error
if (FormsGetErr) done(FormsGetErr);
// Get Forms list
var Forms = FormsGetRes.body;
// Set assertions
(Forms[0].admin).should.equal(userId);
(Forms[0].title).should.match('Form Title');
// Call the assertion callback
done();
});
});
});
});
it('should not be able to create a Form if not logged in', function(done) {
agent.post('/forms')
.send({form: _Form})
.expect(401)
.end(function(FormSaveErr, FormSaveRes) {
(FormSaveRes.body.message).should.equal('User is not logged in');
// Call the assertion callback
done(FormSaveErr);
});
});
it('should not be able to get list of users\' Forms if not logged in', function(done) {
agent.get('/forms')
.expect(401)
.end(function(FormSaveErr, FormSaveRes) {
(FormSaveRes.body.message).should.equal('User is not logged in');
// Call the assertion callback
done(FormSaveErr);
});
});
it('should not be able to save a Form if no title is provided', function(done) {
// Set Form with a invalid title field
_Form.title = '';
agent.post('/auth/signin')
.send(credentials)
.expect('Content-Type', /json/)
.expect(200)
.end(function(signinErr, signinRes) {
// Handle signin error
if (signinErr) done(signinErr);
// Save a new Form
agent.post('/forms')
.send({form: _Form})
.expect(400)
.end(function(FormSaveErr, FormSaveRes) {
// Set message assertion
(FormSaveRes.body.message).should.equal('Form Title cannot be blank');
// Handle Form save error
done();
});
});
});
it('should be able to update a Form if signed in', function(done) {
agent.post('/auth/signin')
.send(credentials)
.expect('Content-Type', /json/)
.expect(200)
.end(function(signinErr, signinRes) {
// Handle signin error
if (signinErr) done(signinErr);
// Save a new Form
agent.post('/forms')
.send({form: _Form})
.expect('Content-Type', /json/)
.expect(200)
.end(function(FormSaveErr, FormSaveRes) {
// Handle Form save error
if (FormSaveErr) done(FormSaveErr);
// Update Form title
_Form.title = 'WHY YOU GOTTA BE SO MEAN?';
// Update an existing Form
agent.put('/forms/' + FormSaveRes.body._id)
.send({form: _Form})
.expect('Content-Type', /json/)
.expect(200)
.end(function(FormUpdateErr, FormUpdateRes) {
// Handle Form update error
if (FormUpdateErr) done(FormUpdateErr);
// Set assertions
(FormUpdateRes.body._id).should.equal(FormSaveRes.body._id);
(FormUpdateRes.body.title).should.match('WHY YOU GOTTA BE SO MEAN?');
// Call the assertion callback
done();
});
});
});
});
it('should be able to read/get a Form if not signed in', function(done) {
// Create new Form model instance
var FormObj = new Form(_Form);
// Save the Form
FormObj.save(function(err, form) {
if(err) done(err);
request(app).get('/forms/' + form._id)
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if(err) done(err)
// Set assertion
(res.body).should.be.an.Object.with.property('title', _Form.title);
// Call the assertion callback
done();
});
});
});
it('should be able to delete a Form if signed in', function(done) {
agent.post('/auth/signin')
.send(credentials)
.expect('Content-Type', /json/)
.expect(200)
.end(function(signinErr, signinRes) {
// Handle signin error
if (signinErr) done(signinErr);
done();
// Save a new Form
// agent.post('/forms')
// .send({form: _Form})
// .expect('Content-Type', /json/)
// .expect(200)
// .end(function(FormSaveErr, FormSaveRes) {
// // Handle Form save error
// if (FormSaveErr) done(FormSaveErr);
// // Delete an existing Form
// agent.delete('/forms/' + FormSaveRes.body._id)
// .send(_Form)
// .expect('Content-Type', /json/)
// .expect(200)
// .end(function(FormDeleteErr, FormDeleteRes) {
// // Handle Form error error
// if (FormDeleteErr) done(FormDeleteErr);
// // Set assertions
// (FormDeleteRes.body._id).should.equal(FormSaveRes.body._id);
// // Call the assertion callback
// done();
// });
// });
});
});
it('should not be able to delete an Form if not signed in', function(done) {
// Set Form user
_Form.admin = user;
// Create new Form model instance
var FormObj = new Form(_Form);
// Save the Form
FormObj.save(function() {
// Try deleting Form
request(app).delete('/forms/' + FormObj._id)
.expect(401)
.end(function(FormDeleteErr, FormDeleteRes) {
// Set message assertion
(FormDeleteRes.body.message).should.match('User is not logged in');
// Handle Form error error
done(FormDeleteErr);
});
});
});
it('should be able to upload a PDF an Form if logged in', function(done) {
agent.post('/auth/signin')
.send(credentials)
.expect('Content-Type', /json/)
.expect(200)
.end(function(signinErr, signinRes) {
// Handle signin error
if (signinErr) done(signinErr);
var user = signinRes.body;
var userId = user._id;
// Save a new Form
agent.post('/forms')
.send({form: _Form})
.expect('Content-Type', /json/)
.expect(200)
.end(function(FormSaveErr, FormSaveRes) {
// Handle Form save error
if (FormSaveErr) done(FormSaveErr);
// Get a list of Forms
agent.get('/forms')
.expect('Content-Type', /json/)
.expect(200)
.end(function(FormsGetErr, FormsGetRes) {
// Handle Form save error
if (FormsGetErr) done(FormsGetErr);
// Get Forms list
var Forms = FormsGetRes.body;
// Set assertions
(Forms[0].admin).should.equal(userId);
(Forms[0].title).should.match('Form Title');
// Call the assertion callback
done();
});
});
});
});
describe('Form Submission tests', function() {
var FormObj, _Submission, submissionSession;
beforeEach(function (done) {
_Form.admin = user;
FormObj = new Form(_Form);
FormObj.save(function(err, form) {
if (err) done(err);
_Submission = {
form_fields: [
{'fieldType':'textfield', 'title':'First Name', 'fieldValue': 'David'},
{'fieldType':'checkbox', 'title':'nascar', 'fieldValue': true},
{'fieldType':'checkbox', 'title':'hockey', 'fieldValue': false}
],
form: form._id,
admin: user._id,
percentageComplete: 100,
timeElapsed: 11.55
};
FormObj = form;
//Setup test session
submissionSession = new Session();
done();
});
});
it('should be able to create a Form Submission without signing in', function(done) {
//Create Submission
submissionSession.post('/forms/' + FormObj._id)
.send(_Submission)
.expect(200)
.end(function(err, res) {
should.not.exist(err);
done();
});
});
it('should be able to get Form Submissions if signed in', function(done) {
submissionSession.post('/auth/signin')
.send(credentials)
.expect('Content-Type', /json/)
.expect(200)
.end(function(signinErr, signinRes) {
should.not.exist(signinErr);
//Create Submission
submissionSession.post('/forms/' + FormObj._id)
.send(_Submission)
.expect(200)
.end(function(err, res) {
should.not.exist(err);
submissionSession.get('/forms/' + FormObj._id + '/submissions')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
// Set assertion
should.not.exist(err);
// Call the assertion callback
done();
});
});
});
});
it('should not be able to get Form Submissions if not signed in', function(done) {
// Attempt to fetch form submissions
submissionSession.get('/forms/' + FormObj._id + '/submissions')
.expect(401)
.end(function(err, res) {
// Set assertions
(res.body.message).should.equal('User is not logged in');
// Call the assertion callback
done();
});
});
it('should not be able to delete Form Submission if not signed in', function(done) {
var SubmissionObj = new FormSubmission(_Submission);
SubmissionObj.save(function (err, submission) {
should.not.exist(err);
var submission_ids = _.pluck([submission], '_id');
// Attempt to delete form submissions
submissionSession.delete('/forms/' + FormObj._id + '/submissions')
.send({deleted_submissions: submission_ids})
.expect(401)
.end(function(err, res) {
// Set assertions
should.not.exist(err);
(res.body.message).should.equal('User is not logged in');
// Call the assertion callback
done();
});
});
});
it('should be able to delete Form Submission if signed in', function(done) {
// Create new FormSubmission model instance
var SubmissionObj = new FormSubmission(_Submission);
SubmissionObj.save(function (err, submission) {
should.not.exist(err);
// Signin as user
submissionSession.post('/auth/signin')
.send(credentials)
.expect('Content-Type', /json/)
.expect(200)
.end(function(signinErr, signinRes) {
// Handle signin error
if (signinErr) done(signinErr);
var submission_ids = _.pluck([submission], '_id');
//Delete form submissions
submissionSession.delete('/forms/' + FormObj._id + '/submissions')
.send({deleted_submissions: submission_ids})
.expect(200)
.end(function(err, res) {
// Set assertions
should.not.exist(err);
(res.text).should.equal('Form submissions successfully deleted');
// Call the assertion callback
done();
});
});
});
});
afterEach(function(done) {//logout current user if there is one
FormSubmission.remove().exec(function() {
Form.remove().exec(function (err) {
submissionSession.destroy();
done();
});
});
});
});
afterEach(function(done) {
User.remove().exec(function() {
Form.remove().exec(done);
});
});
});

View file

@ -16,22 +16,20 @@ var user, user2;
* Unit tests * Unit tests
*/ */
describe('User Model Unit Tests:', function() { describe('User Model Unit Tests:', function() {
before(function(done) { beforeEach(function(done) {
user = new User({ user = new User({
firstName: 'Full', firstName: 'Full',
lastName: 'Name', lastName: 'Name',
displayName: 'Full Name',
email: 'test@test.com', email: 'test@test.com',
username: 'username', username: 'test@test.com',
password: 'password', password: 'password',
provider: 'local' provider: 'local'
}); });
user2 = new User({ user2 = new User({
firstName: 'Full', firstName: 'Full',
lastName: 'Name', lastName: 'Name',
displayName: 'Full Name',
email: 'test@test.com', email: 'test@test.com',
username: 'username', username: 'test@test.com',
password: 'password', password: 'password',
provider: 'local' provider: 'local'
}); });
@ -69,7 +67,24 @@ describe('User Model Unit Tests:', function() {
}); });
}); });
after(function(done) { describe('Method findUniqueUsername', function() {
beforeEach(function(done) {
User.find({}, function(err, users) {
users.should.have.length(0);
user.save(done);
});
});
it('should be able to find unique version of existing username without problems', function(done) {
User.findUniqueUsername(user.username, null, function (availableUsername) {
availableUsername.should.not.equal(user.username);
done();
});
});
});
afterEach(function(done) {
User.remove().exec(done); User.remove().exec(done);
}); });
}); });

View file

@ -0,0 +1,118 @@
'use strict';
var should = require('should'),
_ = require('lodash'),
app = require('../../server'),
request = require('supertest'),
Session = require('supertest-session')({
app: app
}),
mongoose = require('mongoose'),
User = mongoose.model('User'),
config = require('../../config/config'),
tmpUser = mongoose.model(config.tempUserCollection),
agent = request.agent(app),
mailosaur = require('mailosaur')(config.mailosaur.key),
mailbox = new mailosaur.Mailbox(config.mailosaur.mailbox_id);
/**
* Globals
*/
var credentials, _User, _Session;
/**
* Form routes tests
*/
describe('User CRUD tests', function() {
var userSession;
beforeEach(function(done) {
//Initialize Session
userSession = new Session();
// Create user credentials
credentials = {
username: 'test@test.com',
password: 'password'
};
// Create a new user
_User = {
firstName: 'Full',
lastName: 'Name',
email: credentials.username,
username: credentials.username,
password: credentials.password,
};
console.info('config.mailosaur.mailbox_id: '+config.mailosaur.mailbox_id)
done();
});
it('should be able to create a temporary (non-activated) User', function(done) {
userSession.post('/auth/signup')
.send(_User)
.expect(200)
.end(function(FormSaveErr, FormSaveRes) {
(FormSaveRes.text).should.equal('An email has been sent to you. Please check it to verify your account.');
tmpUser.findOne({username: _User.username}, function (err, user) {
should.not.exist(err);
should.exist(user);
console.log(user);
(_User.username).shoud.equal(user.username);
(_User.firstName).shoud.equal(user.firstName);
(_User.lastName).shoud.equal(user.lastName);
// Call the assertion callback
done();
});
});
});
it('should be able to create and activate/verify a User Account', function(done) {
credentials.username = _User.email = _User.username = 'testUserCreation.be1e58fb@mailosaur.in';
userSession.post('/auth/signup')
.send(_User)
.expect(200)
.end(function(FormSaveErr, FormSaveRes) {
should.not.exist(FormSaveErr);
(FormSaveRes.text).should.equal('An email has been sent to you. Please check it to verify your account.');
mailbox.getEmails(_User.email,
function(err, emails) {
should.not.exist(err);
email = emails[0];
console.log(email);
done();
// userSession.get('/auth/verify/'+token)
// .send(_User)
// .expect(200, 'User successfully verified')
// .end(function (VerifyErr, VerifyRes) {
// should.not.exist(VerifyErr);
// });
}
);
});
});
afterEach(function(done) {
User.remove().exec(function () {
tmpUser.remove().exec(function(){
mailbox.deleteAllEmail(function (err, body) {
console.log(err);
userSession.destroy();
});
});
});
});
});

View file

@ -64,7 +64,7 @@
<!-- </section> --> <!-- </section> -->
</section> </section>
<script src="https://rawgithub.com/eligrey/FileSaver.js/master/FileSaver.js" type="text/javascript"></script> <script src="lib/file-saver.js/FileSaver.js" type="text/javascript"></script>
<!--Embedding The User Object--> <!--Embedding The User Object-->
<script type="text/javascript"> <script type="text/javascript">
var user = {{ user | json | safe }}; var user = {{ user | json | safe }};

View file

@ -16,6 +16,8 @@ var _ = require('lodash'),
require('./env/all'), require('./env/all'),
require('./env/' + process.env.NODE_ENV) || {} require('./env/' + process.env.NODE_ENV) || {}
); );
//Load keys from api_keys.js if file exists
if( fs.existsSync('./env/api_keys.js') ){ if( fs.existsSync('./env/api_keys.js') ){
module.exports = _.merge( module.exports = _.merge(
exports, exports,

8
config/env/all.js vendored
View file

@ -10,6 +10,12 @@ module.exports = {
templateEngine: 'swig', templateEngine: 'swig',
baseUrl: '', baseUrl: '',
tempUserCollection: 'temporary_users',
mailosaur: {
key: process.env.MAILOSAUR_KEY || '',
mailbox_id: process.env.MAILOSAUR_MAILBOX || '',
},
//Sentry DSN Client Key //Sentry DSN Client Key
DSN: 'http://db01e03015ce48e2b68240ea8254b17c:5d878e9bb6c6488fbb70fb81295ee700@sentry.polydaic.com/1', DSN: 'http://db01e03015ce48e2b68240ea8254b17c:5d878e9bb6c6488fbb70fb81295ee700@sentry.polydaic.com/1',
@ -33,7 +39,7 @@ module.exports = {
maxAge: null, maxAge: null,
// To set the cookie in a specific domain uncomment the following // To set the cookie in a specific domain uncomment the following
// setting: // setting:
// domain: 'yourdomain.com' // domain: 'forms.polydaic.com'
}, },
/* /*

View file

@ -18,6 +18,9 @@ module.exports = {
stream: 'access.log' stream: 'access.log'
} }
}, },
sessionCookie: {
domain: 'forms.polydaic.com'
},
assets: { assets: {
// lib: { // lib: {
// css: [ // css: [

44
config/env/secure.js vendored
View file

@ -19,21 +19,37 @@ module.exports = {
stream: 'access.log' stream: 'access.log'
} }
}, },
sessionCookie: {
path: '/',
httpOnly: false,
// If secure is set to true then it will cause the cookie to be set
// only when SSL-enabled (HTTPS) is used, and otherwise it won't
// set a cookie. 'true' is recommended yet it requires the above
// mentioned pre-requisite.
secure: true,
// Only set the maxAge to null if the cookie shouldn't be expired
// at all. The cookie will expunge when the browser is closed.
maxAge: null,
// To set the cookie in a specific domain uncomment the following
// setting:
domain: 'forms.polydaic.com'
},
assets: { assets: {
lib: { // lib: {
css: [ // css: [
'public/lib/bootstrap/dist/css/bootstrap.min.css', // 'public/lib/bootstrap/dist/css/bootstrap.min.css',
'public/lib/bootstrap/dist/css/bootstrap-theme.min.css', // 'public/lib/bootstrap/dist/css/bootstrap-theme.min.css',
], // ],
js: [ // js: [
'public/lib/angular/angular.min.js', // 'public/lib/angular/angular.min.js',
'public/lib/angular-resource/angular-resource.min.js', // 'public/lib/angular-resource/angular-resource.min.js',
'public/lib/angular-animate/angular-animate.min.js', // 'public/lib/angular-animate/angular-animate.min.js',
'public/lib/angular-ui-router/release/angular-ui-router.min.js', // 'public/lib/angular-ui-router/release/angular-ui-router.min.js',
'public/lib/angular-ui-utils/ui-utils.min.js', // 'public/lib/angular-ui-utils/ui-utils.min.js',
'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js' // 'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js'
] // ]
}, // },
css: 'public/dist/application.min.css', css: 'public/dist/application.min.css',
js: 'public/dist/application.min.js' js: 'public/dist/application.min.js'
}, },

View file

@ -27,17 +27,52 @@ var fs = require('fs-extra'),
path = require('path'), path = require('path'),
client = new raven.Client(config.DSN); client = new raven.Client(config.DSN);
// NEV setup and configuration ================
var config_nev = function () {
var nev = require('email-verification'),
mongoose = require('mongoose'),
User = mongoose.model('User');
nev.configure({
persistentUserModel: User,
tempUserCollection: config.tempUserCollection,
expirationTime: 1800, // 30 minutes
verificationURL: config.baseUrl+'/#!/verify/${URL}',
transportOptions: config.mailer.options,
verifyMailOptions: {
from: config.mailer.from,
subject: 'Confirm your account',
html: '<p>Please verify your account by clicking <a href="${URL}">this link</a>. If you are unable to do so, copy and ' +
'paste the following link into your browser:</p><p>${URL}</p>',
text: 'Please verify your account by clicking the following link, or by copying and pasting it into your browser: ${URL}'
},
confirmMailOptions: {
from: config.mailer.from,
subject: 'Successfully verified!',
html: '<p>Your account has been successfully verified.</p>',
text: 'Your account has been successfully verified.'
},
});
nev.generateTempUserModel(User);
};
module.exports = function(db) { module.exports = function(db) {
// Initialize express app // Initialize express app
var app = express(); var app = express();
// Globbing model files // Globbing model files
config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) { config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
require(path.resolve(modelPath)); require(path.resolve(modelPath));
}); });
//Configure Node-Email-Verification
config_nev();
// Setting application local variables // Setting application local variables
app.locals.title = config.app.title; app.locals.title = config.app.title;
app.locals.description = config.app.description; app.locals.description = config.app.description;
@ -110,7 +145,6 @@ module.exports = function(db) {
app.use('/', express.static(path.resolve('./public'))); app.use('/', express.static(path.resolve('./public')));
app.use('/uploads', express.static(path.resolve('./uploads'))); app.use('/uploads', express.static(path.resolve('./uploads')));
var formCtrl = require('../app/controllers/forms.server.controller');
// Setting the pdf upload route and folder // Setting the pdf upload route and folder
app.use(multer({ dest: config.tmpUploadPath, app.use(multer({ dest: config.tmpUploadPath,
rename: function (fieldname, filename) { rename: function (fieldname, filename) {
@ -156,7 +190,7 @@ module.exports = function(db) {
}); });
// Add headers for Sentry // Add headers for Sentry
app.use(function (req, res, next) { app.use(function (req, res, next) {
// Website you wish to allow to connect // Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://sentry.polydaic.com'); res.setHeader('Access-Control-Allow-Origin', 'http://sentry.polydaic.com');

View file

@ -0,0 +1,140 @@
<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions name="DemographicWsService" targetNamespace="http://ws.oscarehr.org/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://ws.oscarehr.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://ws.oscarehr.org/" version="1.0" xmlns:tns="http://ws.oscarehr.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="getDemographic" type="tns:getDemographic"/>
<xs:element name="getDemographicByMyOscarUserName" type="tns:getDemographicByMyOscarUserName"/>
<xs:element name="getDemographicByMyOscarUserNameResponse" type="tns:getDemographicByMyOscarUserNameResponse"/>
<xs:element name="getDemographicResponse" type="tns:getDemographicResponse"/>
<xs:complexType name="getDemographic">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:int"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getDemographicResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="tns:demographicTransfer"/>
</xs:sequence>
</xs:complexType>
<xs:complexType final="extension restriction" name="demographicTransfer">
<xs:sequence>
<xs:element name="activeCount" type="xs:int"/>
<xs:element minOccurs="0" name="address" type="xs:string"/>
<xs:element minOccurs="0" name="alias" type="xs:string"/>
<xs:element minOccurs="0" name="anonymous" type="xs:string"/>
<xs:element minOccurs="0" name="chartNo" type="xs:string"/>
<xs:element minOccurs="0" name="children" type="xs:string"/>
<xs:element minOccurs="0" name="citizenship" type="xs:string"/>
<xs:element minOccurs="0" name="city" type="xs:string"/>
<xs:element minOccurs="0" name="dateJoined" type="xs:dateTime"/>
<xs:element minOccurs="0" name="dateOfBirth" type="xs:string"/>
<xs:element minOccurs="0" name="demographicNo" type="xs:int"/>
<xs:element minOccurs="0" name="displayName" type="xs:string"/>
<xs:element minOccurs="0" name="effDate" type="xs:dateTime"/>
<xs:element minOccurs="0" name="email" type="xs:string"/>
<xs:element minOccurs="0" name="endDate" type="xs:dateTime"/>
<xs:element minOccurs="0" name="familyDoctor" type="xs:string"/>
<xs:element minOccurs="0" name="firstName" type="xs:string"/>
<xs:element minOccurs="0" name="hcRenewDate" type="xs:dateTime"/>
<xs:element minOccurs="0" name="hcType" type="xs:string"/>
<xs:element minOccurs="0" name="headRecord" type="xs:int"/>
<xs:element minOccurs="0" name="hin" type="xs:string"/>
<xs:element name="hsAlertCount" type="xs:int"/>
<xs:element minOccurs="0" name="lastName" type="xs:string"/>
<xs:element minOccurs="0" name="lastUpdateDate" type="xs:dateTime"/>
<xs:element minOccurs="0" name="lastUpdateUser" type="xs:string"/>
<xs:element minOccurs="0" name="links" type="xs:string"/>
<xs:element minOccurs="0" name="monthOfBirth" type="xs:string"/>
<xs:element minOccurs="0" name="myOscarUserName" type="xs:string"/>
<xs:element minOccurs="0" name="officialLanguage" type="xs:string"/>
<xs:element minOccurs="0" name="patientStatus" type="xs:string"/>
<xs:element minOccurs="0" name="patientStatusDate" type="xs:dateTime"/>
<xs:element minOccurs="0" name="pcnIndicator" type="xs:string"/>
<xs:element minOccurs="0" name="phone" type="xs:string"/>
<xs:element minOccurs="0" name="phone2" type="xs:string"/>
<xs:element minOccurs="0" name="postal" type="xs:string"/>
<xs:element minOccurs="0" name="previousAddress" type="xs:string"/>
<xs:element minOccurs="0" name="providerNo" type="xs:string"/>
<xs:element minOccurs="0" name="province" type="xs:string"/>
<xs:element minOccurs="0" name="rosterDate" type="xs:dateTime"/>
<xs:element minOccurs="0" name="rosterStatus" type="xs:string"/>
<xs:element minOccurs="0" name="rosterTerminationDate" type="xs:dateTime"/>
<xs:element minOccurs="0" name="rosterTerminationReason" type="xs:string"/>
<xs:element minOccurs="0" name="sex" type="xs:string"/>
<xs:element minOccurs="0" name="sexDesc" type="xs:string"/>
<xs:element minOccurs="0" name="sin" type="xs:string"/>
<xs:element minOccurs="0" name="sourceOfIncome" type="xs:string"/>
<xs:element minOccurs="0" name="spokenLanguage" type="xs:string"/>
<xs:element minOccurs="0" name="title" type="xs:string"/>
<xs:element minOccurs="0" name="ver" type="xs:string"/>
<xs:element minOccurs="0" name="yearOfBirth" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getDemographicByMyOscarUserName">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getDemographicByMyOscarUserNameResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="tns:demographicTransfer"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="getDemographicByMyOscarUserNameResponse">
<wsdl:part element="tns:getDemographicByMyOscarUserNameResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getDemographicByMyOscarUserName">
<wsdl:part element="tns:getDemographicByMyOscarUserName" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getDemographicResponse">
<wsdl:part element="tns:getDemographicResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getDemographic">
<wsdl:part element="tns:getDemographic" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="DemographicWs">
<wsdl:operation name="getDemographic">
<wsdl:input message="tns:getDemographic" name="getDemographic">
</wsdl:input>
<wsdl:output message="tns:getDemographicResponse" name="getDemographicResponse">
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getDemographicByMyOscarUserName">
<wsdl:input message="tns:getDemographicByMyOscarUserName" name="getDemographicByMyOscarUserName">
</wsdl:input>
<wsdl:output message="tns:getDemographicByMyOscarUserNameResponse" name="getDemographicByMyOscarUserNameResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="DemographicWsServiceSoapBinding" type="tns:DemographicWs">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getDemographic">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="getDemographic">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getDemographicResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getDemographicByMyOscarUserName">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="getDemographicByMyOscarUserName">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getDemographicByMyOscarUserNameResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="DemographicWsService">
<wsdl:port binding="tns:DemographicWsServiceSoapBinding" name="DemographicWsPort">
<soap:address location="https://secure11.oscarhost.ca/kensington/ws/DemographicService"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

8
docs/Oscar Credentials Normal file
View file

@ -0,0 +1,8 @@
SOAP API Oscar URL
https://secure11.oscarhost.ca/kensington/ws
OSCARHOST DEMO ACCOUNT:
username: davieb
password: Temppass1
2ndlevel: 6448

View file

@ -60,6 +60,7 @@
"karma-phantomjs-launcher": "~0.1.2", "karma-phantomjs-launcher": "~0.1.2",
"load-grunt-tasks": "~1.0.0", "load-grunt-tasks": "~1.0.0",
"lodash": "~2.4.1", "lodash": "~2.4.1",
"mailosaur": "^1.0.1",
"main-bower-files": "~2.8.2", "main-bower-files": "~2.8.2",
"method-override": "~2.3.0", "method-override": "~2.3.0",
"mocha": ">=1.20.0", "mocha": ">=1.20.0",
@ -82,6 +83,7 @@
"shortid": "^2.2.2", "shortid": "^2.2.2",
"should": "~4.1.0", "should": "~4.1.0",
"supertest": "~0.14.0", "supertest": "~0.14.0",
"supertest-session": "^1.0.0",
"swig": "~1.4.1", "swig": "~1.4.1",
"then-fs": "~2.0.0" "then-fs": "~2.0.0"
}, },

View file

@ -33,15 +33,28 @@ angular.module('forms').controller('ListFormsController', ['$rootScope', '$scope
$state.go(route, {'formId': id}, {reload: true}); $state.go(route, {'formId': id}, {reload: true});
}; };
$scope.duplicate = function(form, form_index){
delete form._id;
$http.post('/forms', {form: form})
.success(function(data, status, headers){
console.log('form duplicated');
$scope.myforms.splice(form_index, 0, data);
}).error(function(errorResponse){
console.log(errorResponse);
$scope.error = errorResponse.data.message;
});
}
// Create new Form // Create new Form
$scope.createNew = function(){ $scope.createNew = function(){
var form = {}; var form = {};
form.title = $scope.myform.name.$modelValue; form.title = $scope.myform.name.$modelValue;
form.language = $scope.myform.language.$modelValue; form.language = $scope.myform.language.$modelValue;
console.log(form); // console.log(form);
$rootScope.showCreateModal = true; $rootScope.showCreateModal = true;
console.log($scope.myform); // console.log($scope.myform);
if($scope.myform.$valid && $scope.myform.$dirty){ if($scope.myform.$valid && $scope.myform.$dirty){
$http.post('/forms', {form: form}) $http.post('/forms', {form: form})
.success(function(data, status, headers){ .success(function(data, status, headers){
@ -58,28 +71,17 @@ angular.module('forms').controller('ListFormsController', ['$rootScope', '$scope
} }
}; };
$scope.remove = function(form_id) { $scope.removeFromList = function(deleted_form_id) {
console.log('Remove existing form'); console.log('Remove existing form');
var form = {};
if(!form_id){
form = CurrentForm.getForm();
if(!form) form = $scope.myform;
}else {
form._id = form_id;
}
$http.delete('/forms/'+form._id) $http.delete('/forms/'+deleted_form_id)
.success(function(data, status, headers){ .success(function(data, status, headers){
console.log('form deleted successfully'); console.log('form deleted successfully');
if(!form_id){
$state.go('listForms', {}, {reload: true});
}
if($scope.myforms.length > 0){ if($scope.myforms.length > 0){
$scope.myforms = _.filter($scope.myforms, function(myform){ $scope.myforms = _.filter($scope.myforms, function(myform){
return myform._id !== form._id; return myform._id !== deleted_form_id;
}); });
} }

View file

@ -154,13 +154,16 @@ form .row.field {
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
} }
div.config-form .row.field {
padding-top:1.5em;
}
div.config-form > .row > .container:nth-of-type(odd){ div.config-form > .row > .container:nth-of-type(odd){
border-right: 1px #ddd solid; border-right: 1px #ddd solid;
/*padding-left: 1em;*/ /*padding-left: 1em;*/
} }
div.config-form .row > .field-input { div.config-form .row > .field-input {
padding-top:1.2em;
padding-left:0.1em; padding-left:0.1em;
} }
div.config-form .row > .field-input label { div.config-form .row > .field-input label {
@ -292,15 +295,15 @@ section > section.public-form {
text-align: center; text-align: center;
border-bottom: 6px inset #ccc; border-bottom: 6px inset #ccc;
background-color: #eee; background-color: #eee;
/*width: 180px;*/ width: 180px;
/*width:100%;*/ /*width:100%;*/
position: relative; position: relative;
height: 0;/*215px;*/ height: 215px;
padding-bottom: 25%; /*padding-bottom: 25%;*/
margin-bottom: 45px; margin-bottom: 45px;
} }
.form-item.create-new input[type='text']{ .form-item.create-new input[type='text']{
width: inherit; /*width: inherit;*/
} }
.form-item.create-new { .form-item.create-new {
@ -322,6 +325,15 @@ section > section.public-form {
} }
/*Modal overlay (for lightbox effect)*/ /*Modal overlay (for lightbox effect)*/
.overlay {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: rgba(256,256,256,0.8);
z-index: 10;
}
.overlay { .overlay {
position: fixed; position: fixed;
top: 0; top: 0;
@ -354,35 +366,40 @@ section > section.public-form {
} }
.form-item.create-new:hover { .form-item.create-new:hover {
/*color: #eee;*/
background-color: rgb(81,81,81); background-color: rgb(81,81,81);
} }
.form-item > .title-row{ .form-item > .row.footer {
position: absolute;
bottom: 0;
left: 30%;
}
.form-item .title-row{
position: relative; position: relative;
top: 15px; top: 15px;
padding-top:3em; padding-top:3em;
padding-bottom:3.65em; padding-bottom:3.65em;
} }
.form-item > .title-row h4 { .form-item .title-row h4 {
font-size: 1.3em; font-size: 1.3em;
} }
.form-item.create-new > .title-row{ .form-item.create-new .title-row{
padding: 0; padding: 0;
/*margin-top:1em; */ /*margin-top:1em; */
} }
.form-item.create-new > .title-row h4 { .form-item.create-new .title-row h4 {
font-size: 7em; font-size: 7em;
} }
.form-item > .details-row{ .form-item .details-row{
margin-top: 3.2em; margin-top: 3.2em;
} }
.form-item > .details-row small { .form-item .details-row small {
font-size: 0.6em; font-size: 0.6em;
} }
.form-item.create-new > .details-row small { .form-item.create-new .details-row small {
font-size: 0.95em; font-size: 0.95em;
} }

View file

@ -35,7 +35,7 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
return false; return false;
}; };
var updateFields = function () { var debounceSave = function () {
$rootScope.saveInProgress = true; $rootScope.saveInProgress = true;
$rootScope[$attrs.autoSaveCallback](false, $rootScope[$attrs.autoSaveCallback](false,
function(err){ function(err){
@ -49,12 +49,12 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
console.error(err); console.error(err);
} }
}); });
} };
$scope.$watch(function(newValue, oldValue) { $scope.$watch(function(newValue, oldValue) {
if($scope.anyDirtyAndTouched($scope.editForm) && !$rootScope.saveInProgress){ if($scope.anyDirtyAndTouched($scope.editForm) && !$rootScope.saveInProgress){
updateFields(); debounceSave();
} }
}); });
@ -84,7 +84,7 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
savePromise = $timeout(function() { savePromise = $timeout(function() {
console.log('Saving Form'); console.log('Saving Form');
updateFields(); debounceSave();
}); });
}else if($rootScope.finishedRender && $rootScope.saveInProgress){ }else if($rootScope.finishedRender && $rootScope.saveInProgress){
$rootScope.saveInProgress = false; $rootScope.saveInProgress = false;

View file

@ -11,10 +11,7 @@ angular.module('forms')
}, },
// transclude: true, // transclude: true,
controller: function($scope){ controller: function($scope){
// Log that the directive has been linked.
// console.log( "Linked: editForm Controller");
/* /*
** Initialize scope with variables ** Initialize scope with variables
*/ */
@ -27,7 +24,7 @@ angular.module('forms')
return type; return type;
}); });
// accordion settings // Accordion settings
$scope.accordion = {}; $scope.accordion = {};
$scope.accordion.oneAtATime = true; $scope.accordion.oneAtATime = true;
@ -39,9 +36,8 @@ angular.module('forms')
*/ */
$scope.dropzone = { $scope.dropzone = {
handle: ' .handle' handle: ' .handle'
} };
// console.log($scope.myform);
// $scope.draggable = { // $scope.draggable = {
// connectWith: ".dropzone", // connectWith: ".dropzone",

View file

@ -26,7 +26,7 @@ angular.module('forms').directive('fieldIconDirective', function($http, $compile
'statement': 'fa fa-quote-left', 'statement': 'fa fa-quote-left',
'yes_no': 'fa fa-toggle-on', 'yes_no': 'fa fa-toggle-on',
'number': 'fa fa-slack' 'number': 'fa fa-slack'
} };
$scope.typeIcon = iconTypeMap[$scope.typeName]; $scope.typeIcon = iconTypeMap[$scope.typeName];
}, },

View file

@ -46,7 +46,7 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
scope.dateOptions = { scope.dateOptions = {
changeYear: true, changeYear: true,
changeMonth: true, changeMonth: true,
altFormat: "mm/dd/yyyy", altFormat: 'mm/dd/yyyy',
yearRange: '1900:-0', yearRange: '1900:-0',
defaultDate: 0, defaultDate: 0,
}; };

View file

@ -13,16 +13,17 @@ angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCo
$scope.selected = null; $scope.selected = null;
$scope.startPage = true; $scope.startPage = true;
timeCounter.startClock()
$rootScope.setActiveField = function (field_id) { $rootScope.setActiveField = function (field_id) {
console.log('form field clicked: '+field_id); console.log('form field clicked: '+field_id);
$scope.selected = field_id; $scope.selected = field_id;
console.log($scope.selected); console.log($scope.selected);
} };
$scope.hideOverlay = function (){ $scope.hideOverlay = function (){
$scope.selected = null; $scope.selected = null;
console.log($scope.myForm); console.log($scope.myForm);
} };
$scope.submit = function(){ $scope.submit = function(){
var _timeElapsed = timeCounter.stopClock(); var _timeElapsed = timeCounter.stopClock();
@ -50,7 +51,7 @@ angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCo
$scope.exitStartPage = function () { $scope.exitStartPage = function () {
$scope.startPage = false; $scope.startPage = false;
} };
$scope.reloadForm = function(){ $scope.reloadForm = function(){
timeCounter.stopClock(); timeCounter.stopClock();

View file

@ -1,5 +1,5 @@
<div class="field row" ng-click="setActiveField(field._id)" ng-if="field.fieldOptions.length > 0"> <div class="field row" ng-click="setActiveField(field._id)" ng-if="field.fieldOptions.length > 0">
<div class="col-xs-12 field-title field-title">{{field.title}} </div> <div class="col-xs-12 field-title field-title">{{field.title}} <span class="required-error" ng-show="field.required && field.fieldValue == 0">(* required)</span> </div>
<div class="col-xs-12 field-input"> <div class="col-xs-12 field-input">
<div ng-repeat="option in field.fieldOptions" class="row-fluid"> <div ng-repeat="option in field.fieldOptions" class="row-fluid">
<label class="btn btn-info col-xs-3"> <label class="btn btn-info col-xs-3">
@ -7,7 +7,7 @@
<span ng-bind="option.option_title"></span> <span ng-bind="option.option_title"></span>
</label> </label>
</div> </div>
<span class="required-error" ng-show="field.required && field.fieldValue == 0">(* required)</span>
</div> </div>
<div class="field row" ng-click="setActiveField(field._id)"> <div class="field row" ng-click="setActiveField(field._id)">

View file

@ -1,5 +1,11 @@
<div class="field row dropdown" ng-click="setActiveField(field._id)" ng-if="field.fieldOptions.length > 0"> <div class="field row dropdown" ng-click="setActiveField(field._id)" ng-if="field.fieldOptions.length > 0">
<div class="col-xs-12 field-title"><h3><span class="fa fa-angle-double-right"></span> {{field.title}} <span class="required-error" ng-show="field.required && !field.fieldValue">*(required)</span></h3></div> <div class="col-xs-12 field-title">
<h3>
<span class="fa fa-angle-double-right"></span>
{{field.title}}
<span class="required-error" ng-show="field.required && !field.fieldValue">*(required)</span>
</h3>
</div>
<div class="col-xs-12 field-input "> <div class="col-xs-12 field-input ">
<select ng-model="field.fieldValue" ng-model-options="{ debounce: 250 }" ng-required="field.required" ng-disabled="field.disabled"> <select ng-model="field.fieldValue" ng-model-options="{ debounce: 250 }" ng-required="field.required" ng-disabled="field.disabled">
<option ng-repeat="option in field.fieldOptions" <option ng-repeat="option in field.fieldOptions"
@ -8,7 +14,6 @@
{{option.option_title}} {{option.option_title}}
</option> </option>
</select> </select>
<span class="required-error" ng-show="field.required && !field.fieldValue">* required</span>
</div> </div>
</div> </div>
<br> <br>

View file

@ -9,6 +9,5 @@
ng-model-options="{ debounce: 250 }" ng-model-options="{ debounce: 250 }"
ng-required="field.required" ng-required="field.required"
ng-disabled="field.disabled"/> ng-disabled="field.disabled"/>
<span class="required-error" ng-show="field.required && !field.fieldValue">* required</span>
</div> </div>
</div> </div>

View file

@ -1,6 +1,6 @@
<div class="row field" ng-if="form.autofillPDFs"> <div class="row field" ng-if="form.autofillPDFs">
<div class="col-sm-4 field-title"> <div class="col-sm-4 field-title">
<h5>{{field.title}}</h5> <h5>{{field.title}} <span class="required-error" ng-show="field.required && field.fieldValue == 0">(* required)</span></h5>
</div> </div>
<div class="col-sm-8 field-input"> <div class="col-sm-8 field-input">
<div class="input-group "> <div class="input-group ">

View file

@ -2,6 +2,5 @@
<div class="col-xs-12 field-title"><h3><span class="fa fa-angle-double-right"></span> {{field.title}} <span class="required-error" ng-show="field.required && !field.fieldValue">*(required)</span></h3></div> <div class="col-xs-12 field-title"><h3><span class="fa fa-angle-double-right"></span> {{field.title}} <span class="required-error" ng-show="field.required && !field.fieldValue">*(required)</span></h3></div>
<div class="col-xs-12 field-input"> <div class="col-xs-12 field-input">
<input ng-focus="setActiveField(field._id)"type="url" class="text-field-input" placeholder="https://example.com/something" value="{{field.fieldValue}}" ng-model="field.fieldValue" ng-model-options="{ debounce: 250 }" ng-required="field.required" ng-disabled="field.disabled"/> <input ng-focus="setActiveField(field._id)"type="url" class="text-field-input" placeholder="https://example.com/something" value="{{field.fieldValue}}" ng-model="field.fieldValue" ng-model-options="{ debounce: 250 }" ng-required="field.required" ng-disabled="field.disabled"/>
<span class="required-error" ng-show="field.required && !field.fieldValue">* required</span>
</div> </div>
</div> </div>

View file

@ -2,6 +2,5 @@
<div class="col-xs-12 field-title"><h3><span class="fa fa-angle-double-right"></span> {{field.title}} <span class="required-error" ng-show="field.required && !field.fieldValue">*(required)</span></h3></div> <div class="col-xs-12 field-title"><h3><span class="fa fa-angle-double-right"></span> {{field.title}} <span class="required-error" ng-show="field.required && !field.fieldValue">*(required)</span></h3></div>
<div class="col-xs-12 field-input"> <div class="col-xs-12 field-input">
<input ng-focus="setActiveField(field._id)"type="password" class="text-field-input" ng-model="field.fieldValue" ng-model-options="{ debounce: 250 }" value="{{field.fieldValue}}" ng-required="field.required" ng-disabled="field.disabled"> <input ng-focus="setActiveField(field._id)"type="password" class="text-field-input" ng-model="field.fieldValue" ng-model-options="{ debounce: 250 }" value="{{field.fieldValue}}" ng-required="field.required" ng-disabled="field.disabled">
<span class="required-error" ng-show="field.required && !field.fieldValue">* required</span>
</div> </div>
</div> </div>

View file

@ -9,6 +9,5 @@
ng-required="field.required" ng-required="field.required"
ng-disabled="field.disabled" ng-disabled="field.disabled"
changeFocus focus-up-id="focusUpButton" focus-down-id="focusDownButton"> changeFocus focus-up-id="focusUpButton" focus-down-id="focusDownButton">
<span class="required-error" ng-show="field.required && !field.fieldValue">* required</span>
</div> </div>
</div> </div>

View file

@ -1,28 +1,12 @@
<div class="config-form container"> <div class="config-form container">
<div class="row"> <div class="row">
<!-- PDF Generation/EMR Settings -->
<div class="col-md-6 col-sm-12 container"> <div class="col-md-6 col-sm-12 container">
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<h2 class="hidden-sm hidden-xs">PDF Generation</h2> <h2 class="hidden-sm hidden-xs">PDF Generation/EMR</h2>
<h3 class="hidden-lg hidden-md">PDF Generation</h3> <h3 class="hidden-lg hidden-md">PDF Generation/EMR</h3>
</div>
</div>
<div class="row field">
<div class="field-title col-sm-6">
<h5>Hide Form Footer?</h5>
</div>
<div class="field-input col-sm-6">
<label>
<input type="radio" data-ng-value="true" ng-model="myform.hideFooter" ng-required="true" />
&nbsp;<span>Yes</span>
</label>
<label>
<input type="radio" data-ng-value="false" ng-model="myform.hideFooter" ng-required="true" />
&nbsp;<span>No</span>
</label>
</div> </div>
</div> </div>
@ -100,6 +84,74 @@
</div> </div>
</div> </div>
<div class="row field">
<div class="field-title col-sm-4">
<h5>EMR API Key</h5>
</div>
<div class="col-sm-8">
<input type="text"
ng-model="myform.EMR.api_key"
value="{{myform.EMR.api_key}}"
style="width: 100%;">
</div>
</div>
<div class="row field">
<div class="field-title col-sm-4">
<h5>EMR URL</h5>
</div>
<div class="col-sm-8">
<input type="link"
ng-model="myform.EMR.url"
value="{{myform.EMR.url}}"
style="width: 100%;">
</div>
</div>
<!-- generate typeform from MedForm yes/no field -->
<div class="row field">
<div class="col-sm-6 field-title">
<h5>Autogenerate Typeform?</h5>
</div>
<div class="col-sm-6 field-input">
<label>
<input type="radio" data-ng-value="true" ng-model="myform.Typeform.isGenerated" ng-required="true" />
&nbsp;<span>Yes</span>
</label>
<label>
<input type="radio" data-ng-value="false" ng-model="myform.Typeform.isGenerated" ng-required="true" />
&nbsp;<span>No</span>
</label>
</div>
</div>
<div class="row field" ng-if="myform.Typeform.isGenerated">
<div class="field-title col-sm-4">
<h5>Typeform.IO Build API Key</h5>
</div>
<div class="col-sm-8">
<input type="text"
ng-model="myform.Typeform.build_api_key"
value="{{myform.Typeform.build_api_key}}"
style="width: 100%;">
</div>
</div>
<div class="row field" ng-if="myform.Typeform.isGenerated">
<div class="field-title col-sm-4">
<h5>Generated Typeform URL</h5>
</div>
<div class="col-sm-8">
<span type="text" ng-bind="myform.Typeform.url"></span>
</div>
</div>
<!-- !!!!!!DAVID: TODO: Finish this so we can upload pdfFieldMap!!!!!!!!! --> <!-- !!!!!!DAVID: TODO: Finish this so we can upload pdfFieldMap!!!!!!!!! -->
<!-- Map form inputs to PDF inputs Field --> <!-- Map form inputs to PDF inputs Field -->
@ -126,11 +178,12 @@
<hr> <hr>
</div> </div>
<!-- Advanced Settings -->
<div class="col-md-5 col-md-offset-1 col-sm-12 container"> <div class="col-md-5 col-md-offset-1 col-sm-12 container">
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<h2 class="hidden-sm hidden-xs">Advanced Settings</h2> <h2 class="hidden-sm hidden-xs">Advanced Settings</h2>
<h3 class="hidden-lg hidden-md">PDF Generation</h3> <h3 class="hidden-lg hidden-md">Advanced Settings</h3>
</div> </div>
</div> </div>
@ -167,26 +220,7 @@
<!-- <span class="required-error" ng-show="field.required && !field.fieldValue">* required</span> --> <!-- <span class="required-error" ng-show="field.required && !field.fieldValue">* required</span> -->
</div> </div>
</div> </div>
<div class="row field">
<div class="field-title col-sm-6">
<h5>Display Start Page?</h5>
</div>
<div class="field-input col-sm-6">
<label>
<input type="radio" data-ng-value="true" ng-model="myform.showStart" ng-required="true" style="background-color:#33CC00;"/>
&nbsp;<span>Yes</span>
</label>
<label>
<input type="radio" data-ng-value="false" ng-model="myform.showStart" ng-required="true" />
&nbsp;<span>No</span>
</label>
<!-- <span class="required-error" ng-show="field.required && !field.fieldValue">* required</span> -->
</div>
</div>
<div class="row field"> <div class="row field">
<div class="col-xs-6 field-title">Language</div> <div class="col-xs-6 field-title">Language</div>
<div class="col-xs-4 field-input"> <div class="col-xs-4 field-input">
@ -200,6 +234,44 @@
<span class="required-error" ng-show="field.required && !field.fieldValue">* required</span> <span class="required-error" ng-show="field.required && !field.fieldValue">* required</span>
</div> </div>
</div> </div>
<div class="row field">
<div class="field-title col-sm-6">
<h5>Hide Form Footer?</h5>
</div>
<div class="field-input col-sm-6">
<label>
<input type="radio" data-ng-value="true" ng-model="myform.hideFooter" ng-required="true" />
&nbsp;<span>Yes</span>
</label>
<label>
<input type="radio" data-ng-value="false" ng-model="myform.hideFooter" ng-required="true" />
&nbsp;<span>No</span>
</label>
</div>
</div>
<div class="row field">
<div class="field-title col-sm-6">
<h5>Display Start Page?</h5>
</div>
<div class="field-input col-sm-6">
<label>
<input type="radio" data-ng-value="true" ng-model="myform.startPage.showStart" ng-required="true" style="background-color:#33CC00;"/>
&nbsp;<span>Yes</span>
</label>
<label>
<input type="radio" data-ng-value="false" ng-model="myform.startPage.showStart" ng-required="true" />
&nbsp;<span>No</span>
</label>
<!-- <span class="required-error" ng-show="field.required && !field.fieldValue">* required</span> -->
</div>
</div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">

View file

@ -5,6 +5,7 @@
<div class="row add-field-title"> <div class="row add-field-title">
<h3 class="col-md-12 hidden-sm hidden-xs">Click to Add New Field</h3> <h3 class="col-md-12 hidden-sm hidden-xs">Click to Add New Field</h3>
<h4 class="col-sm-12 hidden-xs hidden-md hidden-lg">Add New Field</h5>
<h5 class="col-sm-12 hidden-sm hidden-md hidden-lg">Add Field</h5> <h5 class="col-sm-12 hidden-sm hidden-md hidden-lg">Add Field</h5>
</div> </div>
<div class="panel-group row" class="draggable" ng-model="addField.types"> <div class="panel-group row" class="draggable" ng-model="addField.types">
@ -27,6 +28,59 @@
<div class="col-sm-12 col-md-10"> <div class="col-sm-12 col-md-10">
<accordion close-others="accordion.oneAtATime" ui-sortable="dropzone" ng-model="myform.form_fields" class="dropzone"> <accordion close-others="accordion.oneAtATime" ui-sortable="dropzone" ng-model="myform.form_fields" class="dropzone">
<accordion-group is-open="accordion[$index].isOpen" on-finish-render="editFormFields" ng-if="form.startPage.showStart">
<accordion-heading>
<div class="handle">
<span style="padding-left:1.2em;">
Start Page
<span ng-show="field.required">*</span>
</span>
<span class="pull-right">
<i class="fa fa-chevron-right" ng-hide="accordion[$index].isOpen">
</i>
<i class="fa fa-chevron-down" ng-show="accordion[$index].isOpen">
</i>
</span>
</div>
</accordion-heading>
<div class="accordion-edit container">
<div class="row">
<div class="col-xs-12">
<h4>Edit StartPage</h4>
<br>
</div>
</div>
<div class="row question">
<div class="col-md-4 col-sm-12">Intro Text:</div>
<div class="col-md-8 col-sm-12">
<input type="text" ng-model="form.startPage.introText" name="titleStartPage" value="{{form.startPage.introText}}" required></div>
</div>
<div class="row"><br></div>
<div class="row description">
<div class="col-md-4 col-sm-12">Intro Description:</div>
<div class="col-md-8 col-sm-12">
<textarea type="text" ng-model="form.startPage.introDescription" name="descriptionStartPage" value="{{form.startPage.introDescription}}"></textarea>
</div>
</div>
<<div class="row"><br></div>
<div class="row description">
<div class="col-md-4 col-sm-12">Button Text:</div>
<div class="col-md-8 col-sm-12">
<textarea type="text" ng-model="form.startPage.buttonText" name="buttonStartPage" value="{{form.startPage.buttonText}}"></textarea>
</div>
</div>
</div>
</accordion-group>
<accordion-group ng-repeat="field in myform.form_fields" is-open="accordion[$index].isOpen" on-finish-render="editFormFields" ng-if="!field.deletePreserved"> <accordion-group ng-repeat="field in myform.form_fields" is-open="accordion[$index].isOpen" on-finish-render="editFormFields" ng-if="!field.deletePreserved">
<accordion-heading> <accordion-heading>

View file

@ -4,20 +4,23 @@
<div ng-show="!form.submitted && startPage && form.showStart" class="form-submitted"> <div ng-show="!form.submitted && startPage && form.showStart" class="form-submitted">
<div class="field row text-center"> <div class="field row text-center">
<div class="col-xs-6 col-xs-offset-3 text-center"> <div class="col-xs-12 text-center">
<h1>Welcome to {{form.title}}</h1> <h1>Welcome to {{form.title}}</h1>
</div> </div>
<!-- <div class="col-xs-10 col-xs-offset-1 text-center">
<p>{{form.startPage.introText}}</p>
</div> -->
</div> </div>
<div class="row form-actions"> <div class="row form-actions">
<p class="text-center col-xs-4 col-xs-offset-4"> <p class="text-center col-xs-4 col-xs-offset-4">
<button class="btn btn-info" type="button"> <button class="btn btn-info" type="button">
<a ng-click="exitStartPage()" style="color:white; font-size: 1.6em; text-decoration: none;" > Continue to Form</a> <a ng-click="exitStartPage()" style="color:white; font-size: 1.6em; text-decoration: none;">Continue to Form</a>
</button> </button>
</p> </p>
</div> </div>
</div> </div>
<div ng-hide="form.submitted || startPage" data-ng-init="initFocus()"> <div ng-show="!form.submitted && !(startPage && form.showStart)" data-ng-init="initFocus()">
<div class="field row" style="padding-bottom:5em;"> <div class="field row" style="padding-bottom:5em;">
<div class="col-sm-10 col-sm-offset-1"> <div class="col-sm-10 col-sm-offset-1">
<h1>{{ form.title }} <span style="font-size:0.8em; color: #bbb;" ng-if="!form.isLive">(private preview)</span></h1> <h1>{{ form.title }} <span style="font-size:0.8em; color: #bbb;" ng-if="!form.isLive">(private preview)</span></h1>
@ -42,7 +45,7 @@
</div> </div>
</div> </div>
<div ng-show="form.submitted && !startPage" class="form-submitted"> <div ng-show="form.submitted" class="form-submitted">
<!-- <div class="field row"> <!-- <div class="field row">
<div class="col-sm-11 col-sm-offset-1"><h1>{{ form.title }}</h1> <div class="col-sm-11 col-sm-offset-1"><h1>{{ form.title }}</h1>

View file

@ -21,7 +21,7 @@
</div> </div>
</div> </div>
<form name="createForm" class="col-xs-6 col-xs-offset-3 col-sm-4 col-sm-offset-1 col-md-3 col-md-offset-1 form-item create-new new-form" ng-show="showCreateModal"> <form name="createForm" class="col-xs-6 col-xs-offset-3 col-sm-4 col-sm-offset-1 col-md-3 col-md-offset-1 form-item create-new new-form" ng-if="showCreateModal">
<div ng-init="setForm(createForm);" style="display:none;"></div> <div ng-init="setForm(createForm);" style="display:none;"></div>
<div class="title-row row"> <div class="title-row row">
<div class="col-xs-5 field-title">Name </div> <div class="col-xs-5 field-title">Name </div>
@ -50,22 +50,34 @@
</div> </div>
</form> </form>
<div data-ng-repeat="form in myforms" class="col-xs-6 col-xs-offset-3 col-sm-4 col-sm-offset-1 col-md-3 col-md-offset-1 form-item"> <div data-ng-repeat="form in myforms" class="col-xs-6 col-xs-offset-3 col-sm-4 col-sm-offset-1 col-md-3 col-md-offset-1 form-item container">
<div class="row">
<span class="pull-right"> <span class="pull-right">
<i style="cursor:pointer;" class="fa fa-trash-o" ng-click="remove(form._id)"></i> <i style="cursor:pointer;" class="fa fa-trash-o" ng-click="removeFromList(form._id)"></i>
<i style="cursor:pointer;" class="fa fa-files-o" ng-click="duplicate(form)"></i>
</span> </span>
</div>
<div class="row">
<a data-ng-href="#!/forms/{{form._id}}/admin" class="title-row col-xs-12"> <a data-ng-href="#!/forms/{{form._id}}/admin" class="title-row col-xs-12">
<h4 class="list-group-item-heading" data-ng-bind="form.title"></h4> <h4 class="list-group-item-heading" data-ng-bind="form.title"></h4>
</a> </a>
<div class="col-xs-12 details-row"> </div>
<!-- <small class="list-group-item-text">
Created on
<span data-ng-bind="form.created | date:'shortDate'"></span>
by
<span data-ng-bind="form.admin.username"></span>
</small> -->
<div class="row footer">
<div class="col-xs-12 details-row">
<small class="list-group-item-text">
Created on
<span data-ng-bind="form.created | date:'shortDate'"></span>
</small>
<!--by
<span data-ng-bind="form.admin.username"></span>
</small> -->
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,33 +1,28 @@
<!-- <link rel="stylesheet" href="./modules/forms/css/myform.css">
-->
<section data-ng-controller="SubmitFormController" class="public-form"> <section data-ng-controller="SubmitFormController" class="public-form">
<form-directive form="myform"></form-directive> <form-directive form="myform"></form-directive>
<section ng-if="!myform.hideFooter" class="navbar navbar-fixed-bottom" style="background-color:rgba(242,242,242,0.5); padding-top:15px;"> <section ng-if="!myform.hideFooter" class="navbar navbar-fixed-bottom" style="background-color:rgba(242,242,242,0.5); padding-top:15px;">
<div class="container" > <div class="container" >
<nav role="navigation"> <div class="row">
<ul class="nav navbar-nav navbar-left" > <div class="col-xs-4 col-xs-offset-4 col-sm-4 col-sm-offset-4 text-center col-md-3 col-md-offset-0" ng-show="!myform.submitted">
<li ng-show="!myform.submitted">
<p class="lead">{{myform | formValidity}} out of {{myform.visible_form_fields.length}} answered</p> <p class="lead">{{myform | formValidity}} out of {{myform.visible_form_fields.length}} answered</p>
</li> </div>
</ul> <div class="hidden-xs hidden-sm col-md-6 col-md-offset-3 row">
<ul class="nav navbar-nav navbar-right"> <div class="col-md-4 col-md-offset-2" ng-if="!authentication.isAuthenticated()">
<li style="padding-right:20px" > <a href="/#!/forms" class="btn btn-default">Create a Medform</a>
<a href="/#!/forms" class="btn btn-default" ng-hide="authentication.isAuthenticated()">Create a Medform</a> </div>
</li> <div class="col-md-4 col-md-offset-2" ng-if="authentication.isAuthenticated()" class="hidden-sm hidden-xs">
<li style="padding-right:20px; padding-bottom:10px;" ng-show="authentication.isAuthenticated()">
<a href="/#!/forms/{{myform._id}}/admin" class="btn btn-default">Edit this Medform</a> <a href="/#!/forms/{{myform._id}}/admin" class="btn btn-default">Edit this Medform</a>
</li> </div>
<li style="padding-left:5px"> <div class="col-md-1 col-md-offset-2" style="padding-left:5px" class="hidden-sm hidden-xs">
<div class="btn btn-info" id="focusDownButton" style="padding: 15px;">\/</div> <div class="btn btn-info" id="focusDownButton">\/</div>
</li> </div>
<li style="padding-left:5px"> <div class="col-md-1" class="hidden-sm hidden-xs">
<div class="btn btn-info" id="focusUpButton" style="padding: 15px">/\</div> <div class="btn btn-info" id="focusUpButton">/\</div>
</li> </div>
</ul> </div>
</nav> </div>
</div> </div>
</section> </section>
</section> </section>

View file

@ -10,7 +10,7 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$loca
$scope.signin = function() { $scope.signin = function() {
User.login($scope.credentials).then( User.login($scope.credentials).then(
function(response) { function(response) {
console.log(response) // console.log(response);
Auth.login(response); Auth.login(response);
$scope.user = $rootScope.user = Auth.ensureHasCurrentUser(User); $scope.user = $rootScope.user = Auth.ensureHasCurrentUser(User);