Merge branch 'fixUXBugs' into addSeleniumTests

This commit is contained in:
David Baldwynn 2017-11-21 15:56:49 -08:00
commit c561130f75
54 changed files with 415 additions and 517 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
selenium
dist
.vagrant
npm-debug.*

View File

@ -21,6 +21,7 @@
"globals": { // Globals variables.
"jasmine": true,
"angular": true,
"devel": false,
"_": true,
"saveAs": true,
"ApplicationConfiguration": true,

View File

@ -34,18 +34,8 @@ exports.deleteSubmissions = function(req, res) {
});
return;
}
form.analytics.visitors = [];
form.save(function(formSaveErr){
if(formSaveErr){
res.status(400).send({
message: errorHandler.getErrorMessage(formSaveErr)
});
return;
}
res.status(200).send('Form submissions successfully deleted');
});
res.status(200).send('Form submissions successfully deleted');
});
};
@ -76,7 +66,7 @@ exports.createSubmission = function(req, res) {
message: errorHandler.getErrorMessage(err)
});
}
var form = req.body
var form = req.body;
var formFieldDict = emailNotifications.createFieldDict(form.form_fields);
async.waterfall([
@ -105,7 +95,6 @@ exports.createSubmission = function(req, res) {
if (form.respondentNotifications && form.respondentNotifications.enabled && form.respondentNotifications.toField) {
form.respondentNotifications.toEmails = formFieldDict[form.respondentNotifications.toField];
debugger;
emailNotifications.send(form.respondentNotifications, formFieldDict, smtpTransport, function(err){
if(err){
return callback({
@ -158,7 +147,7 @@ exports.getVisitorData = function(req, res) {
},
{
$facet: {
"deviceStatistics": [
'deviceStatistics': [
{
$unwind: '$analytics.visitors'
},
@ -188,22 +177,22 @@ exports.getVisitorData = function(req, res) {
},
{
$group: {
_id: "$deviceType",
total_time: { $sum: "$SubmittedTimeElapsed" },
responses: { $sum: "$SubmittedResponses" },
_id: '$deviceType',
total_time: { $sum: '$SubmittedTimeElapsed' },
responses: { $sum: '$SubmittedResponses' },
visits: { $sum: 1 }
}
},
{
$project: {
total_time: "$total_time",
responses: "$responses",
visits: "$visits",
total_time: '$total_time',
responses: '$responses',
visits: '$visits',
average_time: {
$cond: [
{ $eq: [ "$responses", 0 ] },
{ $eq: [ '$responses', 0 ] },
0,
{ $divide: ["$total_time", "$responses"] }
{ $divide: ['$total_time', '$responses'] }
]
},
conversion_rate: {
@ -211,9 +200,9 @@ exports.getVisitorData = function(req, res) {
100,
{
$cond: [
{ $eq: [ "$visits", 0 ] },
{ $eq: [ '$visits', 0 ] },
0,
{ $divide: ["$responses", "$visits"] }
{ $divide: ['$responses', '$visits'] }
]
}
]
@ -221,7 +210,7 @@ exports.getVisitorData = function(req, res) {
}
}
],
"globalStatistics": [
'globalStatistics': [
{
$unwind: '$analytics.visitors'
},
@ -252,22 +241,22 @@ exports.getVisitorData = function(req, res) {
{
$group: {
_id: null,
total_time: { $sum: "$SubmittedTimeElapsed" },
responses: { $sum: "$SubmittedResponses" },
total_time: { $sum: '$SubmittedTimeElapsed' },
responses: { $sum: '$SubmittedResponses' },
visits: { $sum: 1 }
}
},
{
$project: {
_id: 0,
total_time: "$total_time",
responses: "$responses",
visits: "$visits",
total_time: '$total_time',
responses: '$responses',
visits: '$visits',
average_time: {
$cond: [
{ $eq: [ "$responses", 0 ] },
{ $eq: [ '$responses', 0 ] },
0,
{ $divide: ["$total_time", "$responses"] }
{ $divide: ['$total_time', '$responses'] }
]
},
conversion_rate: {
@ -275,9 +264,9 @@ exports.getVisitorData = function(req, res) {
100,
{
$cond: [
{ $eq: [ "$visits", 0 ] },
{ $eq: [ '$visits', 0 ] },
0,
{ $divide: ["$responses", "$visits"] }
{ $divide: ['$responses', '$visits'] }
]
}
]
@ -375,8 +364,8 @@ exports.update = function(req, res) {
var form = req.form;
var updatedForm = req.body.form;
if(!form.analytics){
if(!form.analytics && req.body.form.analytics){
form.analytics = {
visitors: [],
gaCode: ''
@ -390,9 +379,18 @@ exports.update = function(req, res) {
diff.applyChange(form._doc, true, change);
});
} else {
if(!updatedForm){
res.status(400).send({
message: 'Updated Form is empty'
});
}
delete updatedForm.__v;
delete updatedForm.lastModified;
delete updatedForm.created;
delete updatedForm.id;
delete updatedForm._id;
delete updatedForm.__v;
//Unless we have 'admin' privileges, updating the form's admin is disabled
if(updatedForm && req.user.roles.indexOf('admin') === -1) {
delete updatedForm.admin;
@ -483,13 +481,16 @@ exports.list = function(req, res) {
});
}
const result_ids = results.map(function(result){ return ''+result._id });
const result_ids = results.map(function(result){
return ''+result._id;
});
var currIndex = -1;
for(var i=0; i<forms.length; i++){
forms[i] = helpers.removeSensitiveModelData('private_form', forms[i]);
currIndex = result_ids.indexOf(forms[i]._id)
currIndex = result_ids.indexOf(forms[i]._id);
if(currIndex > -1){
forms[i].submissionNum = results[currIndex].responses;
} else {
@ -564,7 +565,8 @@ exports.formByIDFast = function(req, res, next, id) {
*/
exports.hasAuthorization = function(req, res, next) {
var form = req.form;
if (req.form.admin.id !== req.user.id && req.user.roles.indexOf('admin') === -1) {
debugger
if (req.form.admin.id !== req.user.id || req.user.roles.indexOf('admin') > -1) {
res.status(403).send({
message: 'User '+req.user.username+' is not authorized to edit Form: '+form.title
});

View File

@ -1,41 +1,35 @@
'use strict';
module.exports = {
removeSensitiveModelData: function(type, object){
var privateFields = {
'public_form': ['__v', 'analytics.visitors', 'analytics.views', 'analytics.conversionRate', 'analytics.fields', 'lastModified', 'created'],
'private_form': ['__v'],
'public_user': ['passwordHash', 'password', 'provider', 'salt', 'lastModified', 'created', 'resetPasswordToken', 'resetPasswordExpires', 'token', 'apiKey', '__v'],
'private_user': ['passwordHash', 'password', 'provider', 'salt', 'resetPasswordToken', 'resetPasswordExpires', 'token', '__v']
};
const constants = require('../libs/constants');
function removeKeysFromDict(dict, keys){
for(var i=0; i<keys.length; i++){
var curr_key = keys[i];
if( dict.hasOwnProperty(curr_key) ){
delete dict[curr_key];
}
module.exports = {
removeKeysFromDict: function(dict, keys){
for(var i=0; i<keys.length; i++){
var curr_key = keys[i];
if( dict.hasOwnProperty(curr_key) ){
delete dict[curr_key];
}
}
},
removeSensitiveModelData: function(type, object){
switch(type){
case 'private_form':
removeKeysFromDict(object, privateFields[type]);
this.removeKeysFromDict(object, constants.privateFields[type]);
if(object.admin){
removeKeysFromDict(object.admin, privateFields.private_user);
this.removeKeysFromDict(object.admin, constants.privateFields.private_user);
}
break;
case 'public_form':
removeKeysFromDict(object, privateFields[type]);
this.removeKeysFromDict(object, constants.privateFields[type]);
if(object.admin){
removeKeysFromDict(object.admin, privateFields.public_user);
this.removeKeysFromDict(object.admin, constants.privateFields.public_user);
}
break;
default:
if(privateFields.hasOwnProperty(type)){
removeKeysFromDict(object, privateFields[type]);
if(constants.privateFields.hasOwnProperty(type)){
this.removeKeysFromDict(object, constants.privateFields[type]);
}
break;
}

View File

@ -1,40 +1,68 @@
'use strict';
module.exports = {
fieldTypes: ['textfield',
'date',
'email',
'legal',
'url',
'textarea',
'statement',
'welcome',
'thankyou',
'file',
'dropdown',
'scale',
'rating',
'radio',
'checkbox',
'hidden',
'yes_no',
'natural',
'stripe',
'number'],
extraneousFormFieldProps: [
'validFieldTypes',
'disabled',
'required',
'isSubmission',
'title',
'fieldOptions',
'ratingOptions',
'logicJump',
'description',
'created',
'lastModified',
'deletePreserved'
],
ratingShapeTypes: ['Heart',
'Star',
'thumbs-up',
'thumbs-down',
'Circle',
'Square',
'Check Circle',
'Smile Outlined',
'Hourglass',
'bell',
'Paper Plane',
'Comment',
'Trash'],
fieldTypes: [
'textfield',
'date',
'email',
'legal',
'textarea',
'link',
'statement',
'dropdown',
'rating',
'radio',
'hidden',
'yes_no',
'number'
],
ratingShapeTypes: [
'Heart',
'Star',
'thumbs-up',
'thumbs-down',
'Circle',
'Square',
'Check Circle',
'Smile Outlined',
'Hourglass',
'bell',
'Paper Plane',
'Comment',
'Trash'
],
ratingShapeTypes: [
'Heart',
'Star',
'thumbs-up',
'thumbs-down',
'Circle',
'Square',
'Check Circle',
'Smile Outlined',
'Hourglass',
'bell',
'Paper Plane',
'Comment',
'Trash'
],
deviceTypes: ['desktop', 'phone', 'tablet', 'other'],
languageTypes: ['en', 'fr', 'es', 'it', 'de'],
@ -55,6 +83,13 @@ module.exports = {
'Deutsch': 'de'
},
privateFields: {
'public_form': ['__v', 'analytics.visitors', 'analytics.views', 'analytics.conversionRate', 'analytics.fields', 'lastModified', 'created'],
'private_form': ['__v'],
'public_user': ['passwordHash', 'password', 'provider', 'salt', 'lastModified', 'created', 'resetPasswordToken', 'resetPasswordExpires', 'token', 'apiKey', '__v'],
'private_user': ['passwordHash', 'password', 'provider', 'salt', 'resetPasswordToken', 'resetPasswordExpires', 'token', '__v']
},
expressionStringTypes: ['field == static',
'field != static',
'field > static',

View File

@ -1,5 +1,5 @@
'use strict';
const jsdom = require("jsdom");
const jsdom = require('jsdom');
var JSDOM = jsdom.JSDOM;
module.exports = {
@ -24,21 +24,12 @@ module.exports = {
var dom = new JSDOM('<!doctype html>'+emailTemplate);
Object.keys(emailTemplateVars).forEach(function (key) {
var elem = dom.window.document.querySelector("span.placeholder-tag[data-id='" + key + "']");
var elem = dom.window.document.querySelector('span.placeholder-tag[data-id=\'' + key + '\']');
if(elem !== null){
elem.outerHTML = emailTemplateVars[key];
}
});
//Removed unused variables
//TODO: Currently querySelectorAll not working in JSDOM
/*
dom.window.document.querySelectorAll("span[data-id]").forEach(function(elem){
if(elem !== null){
elem.outerHTML = '';
}
})
*/
if(onlyText){
return dom.window.document.documentElement.textContent;
}
@ -48,8 +39,8 @@ module.exports = {
createFieldDict: function(form_fields){
var formFieldDict = {};
form_fields.forEach(function(field){
if(field.hasOwnProperty('globalId') && field.hasOwnProperty('fieldValue')){
formFieldDict[field.globalId+''] = field.fieldValue+'';
if(field.hasOwnProperty('fieldValue') && field.hasOwnProperty('_id')){
formFieldDict[field._id] = String(field.fieldValue);
}
});
return formFieldDict;

View File

@ -13,10 +13,6 @@ var mongoose = require('mongoose'),
//Mongoose Models
var FieldSchema = require('./form_field.server.model.js');
var FormSubmissionSchema = require('./form_submission.server.model.js'),
FormSubmission = mongoose.model('FormSubmission', FormSubmissionSchema);
var ButtonSchema = new Schema({
url: {
type: String,
@ -97,18 +93,10 @@ var FormSchema = new Schema({
},
visitors: [VisitorDataSchema]
},
form_fields: {
type: [FieldSchema],
default: []
},
submissions: {
type: [{
type: Schema.Types.ObjectId,
ref: 'FormSubmission'
}],
default: []
},
admin: {
type: Schema.Types.ObjectId,
ref: 'User',
@ -240,30 +228,6 @@ FormSchema.plugin(timeStampPlugin, {
useVirtual: false
});
function getDeletedIndexes(needle, haystack){
var deletedIndexes = [];
if(haystack.length > 0){
for(var i = 0; i < needle.length; i++){
if(haystack.indexOf(needle[i]) === -1){
deletedIndexes.push(i);
}
}
}
return deletedIndexes;
}
function formFieldsAllHaveIds(form_fields){
if(form_fields){
for(var i=0; i<form_fields.length; i++){
if(form_fields[i] && !form_fields[i].hasOwnProperty('_id') && !form_fields[i].hasOwnProperty('globalId')){
return false;
}
}
}
return true;
}
FormSchema.pre('save', function (next) {
if(this.form_fields && this.form_fields.length){
this.form_fields = this.form_fields.filter(function(field){
@ -273,119 +237,6 @@ FormSchema.pre('save', function (next) {
next();
});
/*
FormSchema.pre('save', function (next) {
var that = this;
var _original;
async.series([
function(cb) {
that.constructor
.findOne({_id: that._id}).exec(function (err, original) {
if (err) {
return cb(err);
} else if (!original){
return next();
} else {
_original = original;
return cb(null);
}
});
},
function(cb) {
if(that.form_fields && that.isModified('form_fields') && formFieldsAllHaveIds(that.toObject().form_fields)){
var current_form = that.toObject(),
old_form_fields = _original.toObject().form_fields,
new_ids = _.map(_.map(current_form.form_fields, 'globalId'), function(id){ return ''+id;}),
old_ids = _.map(_.map(old_form_fields, 'globalId'), function(id){ return ''+id;}),
deletedIds = getDeletedIndexes(old_ids, new_ids);
//Check if any form_fileds were deleted
if( deletedIds.length > 0 ){
var modifiedSubmissions = [];
async.forEachOfSeries(deletedIds,
function (deletedIdIndex, key, cb_id) {
var deleted_id = old_ids[deletedIdIndex];
//Find FormSubmissions that contain field with _id equal to 'deleted_id'
FormSubmission.
find({ form: that, form_fields: {$elemMatch: {globalId: deleted_id} } }).
exec(function(err, submissions){
if(err) {
return cb_id(err);
}
//Preserve fields that have at least one submission
if (submissions.length) {
//Add submissions
modifiedSubmissions.push.apply(modifiedSubmissions, submissions);
}
return cb_id(null);
});
},
function (err) {
if(err){
console.error(err.message);
return cb(err);
}
//Iterate through all submissions with modified form_fields
async.forEachOfSeries(modifiedSubmissions, function (submission, key, callback) {
var submission_form_fields = submission.toObject().form_fields;
var currentform_form_fields = that.toObject().form_fields;
//Iterate through ids of deleted fields
for (var i = 0; i < deletedIds.length; i++) {
var index = _.findIndex(submission_form_fields, function (field) {
var tmp_id = field.globalId + '';
return tmp_id === old_ids[deletedIds[i]];
});
var deletedField = submission_form_fields[index];
//Hide field if it exists
if (deletedField) {
//Delete old form_field
submission_form_fields.splice(index, 1);
deletedField.deletePreserved = true;
//Move deleted form_field to start
submission_form_fields.unshift(deletedField);
currentform_form_fields.unshift(deletedField);
}
}
submission.form_fields = submission_form_fields;
that.form_fields = currentform_form_fields;
return callback(null);
}, function (err) {
return cb(err);
});
});
} else {
return cb(null);
}
} else {
return cb(null);
}
}
],
function(err){
if(err){
return next(err);
}
next();
});
});
*/
FormSchema.index({created: 1});
mongoose.model('Form', FormSchema);

View File

@ -49,13 +49,6 @@ function BaseFieldSchema(){
Schema.apply(this, arguments);
this.add({
newOptionSchema: {
type: Boolean,
default: false
},
globalId: {
type: String,
},
isSubmission: {
type: Boolean,
default: false
@ -151,21 +144,13 @@ FormFieldSchema.pre('validate', function(next) {
if(this.fieldOptions && this.fieldOptions.length > 0){
error.errors.ratingOptions = new mongoose.Error.ValidatorError({path:'fieldOptions', message: 'fieldOptions are only allowed for type dropdown, checkbox or radio fields.', type: 'notvalid', value: this.ratingOptions});
console.error(error);
return(next(error));
return next(error);
}
}
return next();
});
//LogicJump Save
FormFieldSchema.pre('save', function(next) {
if(!this.globalId){
this.globalId = tokgen();
}
next();
});
//Submission fieldValue correction
FormFieldSchema.pre('save', function(next) {
if(this.fieldType === 'dropdown' && this.isSubmission){

View File

@ -6,7 +6,9 @@
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
timeStampPlugin = require('../libs/timestamp.server.plugin'),
FieldSchema = require('./form_field.server.model.js');
FieldSchema = require('./form_field.server.model'),
helpers = require('../controllers/helpers.server.controller'),
constants = require('../libs/constants');
/**
* Form Submission Schema
@ -55,18 +57,7 @@ FormSubmissionSchema.pre('save', function (next) {
this.form_fields[i].fieldValue = this.form_fields[i].fieldValue.option_value;
}
delete this.form_fields[i].validFieldTypes;
delete this.form_fields[i].disabled;
delete this.form_fields[i].required;
delete this.form_fields[i].isSubmission;
delete this.form_fields[i].title;
delete this.form_fields[i].fieldOptions;
delete this.form_fields[i].ratingOptions;
delete this.form_fields[i].logicJump;
delete this.form_fields[i].description;
delete this.form_fields[i].created;
delete this.form_fields[i].lastModified;
delete this.form_fields[i].deletePreserved;
helpers.removeKeysFromDict(form_fields[i], constants.extraneousFormFieldProps);
}
next();
});
@ -77,19 +68,7 @@ FormSubmissionSchema.path('form_fields', {
form_fields[i].isSubmission = true;
form_fields[i]._id = new mongoose.mongo.ObjectID();
delete form_fields[i].validFieldTypes;
delete form_fields[i].disabled;
delete form_fields[i].required;
delete form_fields[i].isSubmission;
delete form_fields[i].title;
delete form_fields[i].fieldOptions;
delete form_fields[i].ratingOptions;
delete form_fields[i].logicJump;
delete form_fields[i].description;
delete form_fields[i].created;
delete form_fields[i].lastModified;
delete form_fields[i].deletePreserved;
helpers.removeKeysFromDict(form_fields[i], constants.extraneousFormFieldProps);
}
return form_fields;
}
@ -101,4 +80,4 @@ FormSubmissionSchema.plugin(timeStampPlugin, {
useVirtual: false
});
module.exports = FormSubmissionSchema;
mongoose.model('FormSubmission', FormSubmissionSchema);

View File

@ -40,8 +40,8 @@ describe('Form Model Unit Tests:', function() {
language: 'en',
form_fields: [
{'fieldType':'textfield', title:'First Name', 'fieldValue': ''},
{'fieldType':'checkbox', title:'nascar', 'fieldValue': ''},
{'fieldType':'checkbox', title:'hockey', 'fieldValue': ''}
{'fieldType':'legal', title:'nascar', 'fieldValue': ''},
{'fieldType':'legal', title:'hockey', 'fieldValue': ''}
]
});
done();

View File

@ -10,7 +10,22 @@ var should = require('should'),
Form = mongoose.model('Form'),
Field = mongoose.model('Field'),
FormSubmission = mongoose.model('FormSubmission'),
async = require('async');
async = require('async'),
_ = require('lodash');
function omitDeep(collection, excludeKeys) {
function omitFn(value) {
if (value && typeof value === 'object') {
excludeKeys.forEach((key) => {
delete value[key];
});
}
}
return _.cloneDeepWith(collection, omitFn);
}
/**
* Globals
@ -24,6 +39,18 @@ var credentials = {
password: 'password'
};
var sampleVisitorData = [{
socketId: 'ntneooe8989eotnoeeo',
referrer: 'http://google.com',
timeElapsed: 89898989,
isSubmitted: true,
language: 'en',
ipAddr: '192.168.1.1',
deviceType: 'desktop',
userAgent: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
filledOutFields: []
}];
/**
* Form routes tests
*/
@ -50,8 +77,8 @@ describe('Form Routes Unit tests', function() {
admin: user.id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'First Name', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'nascar', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'hockey', 'fieldValue': ''})
new Field({'fieldType':'legal', 'title':'nascar', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'hockey', 'fieldValue': ''})
],
isLive: true
};
@ -91,7 +118,7 @@ describe('Form Routes Unit tests', function() {
FormObj.save(function(err, form) {
if(err) return done(err);
userSession.get('/subdomain/' + credentials.username + '/forms/' + form._id + '/render')
userSession.get('/forms/' + form._id + '/render')
.expect(200)
.end(function(err, res) {
if(err) return done(err)
@ -114,7 +141,7 @@ describe('Form Routes Unit tests', function() {
FormObj.save(function(err, form) {
if(err) return done(err);
userSession.get('/subdomain/' + credentials.username + '/forms/' + form._id + '/render')
userSession.get('/forms/' + form._id + '/render')
.expect(401, {message: 'Form is Not Public'})
.end(function(err, res) {
done(err);
@ -315,8 +342,8 @@ describe('Form Routes Unit tests', function() {
admin: user.id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'First Name', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'nascar', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'hockey', 'fieldValue': ''})
new Field({'fieldType':'legal', 'title':'nascar', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'hockey', 'fieldValue': ''})
],
isLive: true
};
@ -327,8 +354,8 @@ describe('Form Routes Unit tests', function() {
admin: user.id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'Last Name', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'formula one', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'football', 'fieldValue': ''})
new Field({'fieldType':'legal', 'title':'formula one', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'football', 'fieldValue': ''})
],
isLive: true
};
@ -364,6 +391,123 @@ describe('Form Routes Unit tests', function() {
});
});
it(' > should preserve visitor data when updating a Form', function(done) {
// Create new Form model instance
var formObject = {
title: 'First Form',
language: 'en',
admin: user.id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'First Name', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'nascar', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'hockey', 'fieldValue': ''})
],
isLive: true,
analytics: {
gaCode: '',
visitors: sampleVisitorData
}
};
var formUpdateObject = {
title: 'Second Form',
language: 'en',
admin: user.id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'Last Name', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'formula one', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'football', 'fieldValue': ''})
],
isLive: true
};
var CurrentForm = new Form(formObject);
// Save the Form
CurrentForm.save(function(err, form) {
if(err) return done(err);
loginSession.put('/forms/' + form.id)
.send({ form: formUpdateObject })
.expect(200)
.end(function(err, res) {
should.not.exist(err);
Form.findById(form.id, function (FormFindErr, UpdatedForm){
should.not.exist(FormFindErr);
should.exist(UpdatedForm);
var updatedFormObj = UpdatedForm.toJSON();
var oldFormObj = CurrentForm.toJSON();
updatedFormObj.analytics.should.deepEqual(oldFormObj.analytics);
done(FormFindErr);
});
});
});
});
it(' > shouldn\'t allow a user to change the id when updating a form', function(done) {
// Create new Form model instance
var formObject = {
title: 'First Form',
language: 'en',
admin: user.id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'First Name', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'nascar', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'hockey', 'fieldValue': ''})
],
isLive: true
};
var formUpdateObject = {
id: mongoose.Types.ObjectId(),
title: 'First Form',
language: 'en',
admin: user.id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'Last Name', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'formula one', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'football', 'fieldValue': ''})
],
isLive: true
};
var CurrentForm = new Form(formObject);
// Save the Form
CurrentForm.save(function(err, InitialForm) {
if(err) return done(err);
loginSession.put('/forms/' + InitialForm.id)
.send({ form: formUpdateObject })
.expect(200)
.end(function(err, OldForm) {
should.not.exist(err);
Form.findById(InitialForm.id, function (FormFindErr, UpdatedForm){
should.not.exist(FormFindErr);
should.exist(UpdatedForm);
var updatedFormObj = UpdatedForm.toJSON();
var oldFormObj = InitialForm.toJSON();
updatedFormObj = omitDeep('lastModified');
oldFormObj = omitDeep('lastModified');
updatedFormObj.should.deepEqual(oldFormObj);
done(FormFindErr);
});
});
});
});
afterEach('should be able to signout user', function(done){
authenticatedSession.get('/auth/signout')
.expect(200)

View File

@ -166,7 +166,7 @@ describe('FormSubmission Model Unit Tests:', function() {
});
it('should be able to find FormSubmission by $elemMatch on form_fields id', function(done){
FormSubmission.findOne({ form: myForm._id, form_fields: {$elemMatch: {globalId: myForm.form_fields[0].globalId} } })
FormSubmission.findOne({ form: myForm.id, form_fields: {$elemMatch: {_id: myForm.form_fields[0]._id} } })
.exec(function(err, submission){
should.not.exist(err);
should.exist(submission);
@ -176,78 +176,6 @@ describe('FormSubmission Model Unit Tests:', function() {
});
});
/*
describe('Test FormField and Submission Logic', function() {
beforeEach(function(done){
//Create Submission
mySubmission = new FormSubmission({
form_fields: _.merge(sampleSubmission, myForm.form_fields),
admin: user,
form: myForm,
timeElapsed: 17.55
});
mySubmission.save(function(err){
should.not.exist(err);
done();
});
});
it('should preserve deleted form_fields that have submissions without any problems', function(done) {
var fieldPropertiesToOmit = ['deletePreserved', 'globalId', 'lastModified', 'created', '_id', 'submissionId', 'isSubmission', 'validFieldTypes', 'title'];
var old_fields = myForm.toObject().form_fields;
var new_form_fields = _.clone(myForm.toObject().form_fields);
new_form_fields.splice(0, 1);
myForm.form_fields = new_form_fields;
myForm.save(function(err, _form) {
should.not.exist(err);
should.exist(_form.form_fields);
var actual_fields = _.deepOmit(_form.toObject().form_fields, fieldPropertiesToOmit);
old_fields = _.deepOmit(old_fields, fieldPropertiesToOmit);
should.deepEqual(actual_fields, old_fields, 'old form_fields not equal to newly saved form_fields');
done();
});
});
it('should delete \'preserved\' form_fields whose submissions have been removed without any problems', function(done) {
var old_fields = myForm.toObject().form_fields;
old_fields.splice(0,1);
var new_form_fields = _.clone(myForm.toObject().form_fields);
new_form_fields.splice(0, 1);
myForm.form_fields = new_form_fields;
myForm.save(function(err, _form){
should.not.exist(err);
should.exist(_form.form_fields);
should.exist(old_fields);
var actual_fields = _.deepOmit(_form.toObject().form_fields, ['lastModified', 'created', '_id']);
old_fields = _.deepOmit(old_fields, ['lastModified', 'created', '_id']);
should.deepEqual(JSON.stringify(actual_fields), JSON.stringify(old_fields)); //'old form_fields not equal to newly saved form_fields');
done();
});
});
afterEach(function(done){
mySubmission.remove(function(){
done();
});
});
});
*/
afterEach(function(done) {
Form.remove().exec(function() {
User.remove().exec(function() {

View File

@ -54,8 +54,8 @@ describe('Form Submission Routes Unit tests', function() {
admin: user._id,
form_fields: [
new Field({'fieldType':'textfield', 'title':'First Name', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'nascar', 'fieldValue': ''}),
new Field({'fieldType':'checkbox', 'title':'hockey', 'fieldValue': ''})
new Field({'fieldType':'legal', 'title':'nascar', 'fieldValue': ''}),
new Field({'fieldType':'legal', 'title':'hockey', 'fieldValue': ''})
],
selfNotifications: {
fromField: mongoose.Types.ObjectId(),
@ -81,8 +81,8 @@ describe('Form Submission Routes Unit tests', function() {
form: form._id,
form_fields: [
{'fieldType':'textfield', 'title':'First Name', 'fieldValue': 'David', _id: '', isSubmission: false, deletePreserved: false},
{'fieldType':'checkbox', 'title':'nascar', 'fieldValue': true, _id: '', isSubmission: false, deletePreserved: true},
{'fieldType':'checkbox', 'title':'hockey', 'fieldValue': false, _id: '', isSubmission: false, deletePreserved: false}
{'fieldType':'legal', 'title':'nascar', 'fieldValue': true, _id: '', isSubmission: false, deletePreserved: true},
{'fieldType':'legal', 'title':'hockey', 'fieldValue': false, _id: '', isSubmission: false, deletePreserved: false}
],
percentageComplete: 100,
timeElapsed: 11.55,
@ -101,8 +101,8 @@ describe('Form Submission Routes Unit tests', function() {
_id: form._id,
form_fields: [
{'fieldType':'textfield', 'title':'First Name', 'fieldValue': 'David', _id: '', isSubmission: false, deletePreserved: false},
{'fieldType':'checkbox', 'title':'nascar', 'fieldValue': true, _id: '', isSubmission: false, deletePreserved: true},
{'fieldType':'checkbox', 'title':'hockey', 'fieldValue': false, _id: '', isSubmission: false, deletePreserved: false}
{'fieldType':'legal', 'title':'nascar', 'fieldValue': true, _id: '', isSubmission: false, deletePreserved: true},
{'fieldType':'legal', 'title':'hockey', 'fieldValue': false, _id: '', isSubmission: false, deletePreserved: false}
],
percentageComplete: 100,
timeElapsed: 11.55,

View File

@ -14,9 +14,9 @@ const should = require('should'),
* Globals
*/
const validFormFields = [
{fieldType:'textfield', title:'First Name', fieldValue: 'John Smith', deletePreserved: false, globalId:'56340745f59a6fc9e22028e9'},
{fieldType:'link', title:'Your Website', fieldValue: 'https://johnsmith.me', deletePreserved: false, globalId:'5c9e22028e907634f45f59a6'},
{fieldType:'number', title:'Your Age', fieldValue: 45, deletePreserved: false, globalId:'56e90745f5934fc9e22028a6'}
{fieldType:'textfield', title:'First Name', fieldValue: 'John Smith', deletePreserved: false, _id:'56340745f59a6fc9e22028e9'},
{fieldType:'link', title:'Your Website', fieldValue: 'https://johnsmith.me', deletePreserved: false, _id:'5c9e22028e907634f45f59a6'},
{fieldType:'number', title:'Your Age', fieldValue: 45, deletePreserved: false, _id:'56e90745f5934fc9e22028a6'}
];
const validFieldDict = {

3
config/env/all.js vendored
View File

@ -10,8 +10,7 @@ module.exports = {
db: {
uri: process.env.MONGOLAB_URI || process.env.MONGODB_URI || 'mongodb://'+ (process.env.DB_PORT_27017_TCP_ADDR || '127.0.0.1') + '/mean',
options: {
user: '',
pass: ''
useMongoClient: true
}
},

View File

@ -6,8 +6,7 @@ module.exports = {
db: {
uri: process.env.MONGODB_URI || 'mongodb://'+( process.env.DB_PORT_27017_TCP_ADDR || '127.0.0.1') +'/mean',
options: {
user: '',
pass: ''
useMongoClient: true
}
},
log: {

View File

@ -4,6 +4,9 @@ module.exports = {
baseUrl: process.env.BASE_URL || process.env.HEROKU_APP_NAME + '.herokuapp.com' || 'tellform.com',
db: {
uri: process.env.MONGODB_URI || process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://' + (process.env.DB_PORT_27017_TCP_ADDR || '127.0.0.1') + '/mean',
options: {
useMongoClient: true
}
},
port: process.env.PORT || 5000,
socketUrl: process.env.SOCKET_URL || 'ws.tellform.com',

4
config/env/test.js vendored
View File

@ -5,8 +5,7 @@ module.exports = {
db: {
uri: 'mongodb://localhost/mean-test',
options: {
user: '',
pass: ''
useMongoClient: true
}
},
port: 3001,
@ -19,6 +18,7 @@ module.exports = {
//stream: 'access.log'
}
},
subdomainsDisabled: true,
app: {
title: 'TellForm Test'
},

View File

@ -238,7 +238,7 @@ module.exports = function(db) {
resave: true,
secret: config.sessionSecret,
store: new MongoStore({
mongooseConnection: db.connection,
mongooseConnection: mongoose.connection,
collection: config.sessionCollection
}),
cookie: config.sessionCookie,

View File

@ -21,7 +21,6 @@ module.exports.isAuthenticatedOrApiKey = function isAuthenticated(req, res, next
}
if (!user) {
console.log('no user for apikey');
return res.status(401).send(info.message || '');
}

View File

@ -34,6 +34,9 @@ angular.module('view-form').config(['$translateProvider', function ($translatePr
ADD_NEW_LINE_INSTR: 'Press SHIFT+ENTER to add a newline',
ERROR: 'Error',
LOADING_LABEL: 'Loading',
WAIT_LABEL: 'Please wait',
FORM_404_HEADER: '404 - Form Does Not Exist',
FORM_404_BODY: 'The form you are trying to access does not exist. Sorry about that!',

View File

@ -33,13 +33,16 @@ angular.module('view-form').config(['$translateProvider', function ($translatePr
OPTION_PLACEHOLDER: 'Tapez ou sélectionnez une option',
ADD_NEW_LINE_INSTR: 'Appuyez sur MAJ + ENTRÉE pour ajouter une nouvelle ligne',
ERROR: 'Erreur',
LOADING_LABEL: 'Chargement',
WAIT_LABEL: "Veuillez patienter",
FORM_404_HEADER: '404 - Le formulaire n\'existe pas',
FORM_404_BODY: 'Le formulaire auquel vous essayez d\'accéder n\'existe pas. Désolé pour ça!',
FORM_UNAUTHORIZED_HEADER: 'Non autorisé à accéder au formulaire',
   FORM_UNAUTHORIZED_BODY1: 'Le formulaire auquel vous essayez d\'accéder est actuellement privé et inaccessible publiquement.',
   FORM_UNAUTHORIZED_BODY2: 'Si vous êtes le propriétaire du formulaire, vous pouvez le définir sur "Public" dans le panneau "Configuration" du formulaire admin.',
   FORM_UNAUTHORIZED_BODY1: 'Le formulaire auquel vous essayez d\'accéder est actuellement privé et inaccessible publiquement.',
   FORM_UNAUTHORIZED_BODY2: 'Si vous êtes le propriétaire du formulaire, vous pouvez le définir sur "Public" dans le panneau "Configuration" du formulaire admin.',
});
}]);

View File

@ -33,13 +33,16 @@ angular.module('view-form').config(['$translateProvider', function ($translatePr
OPTION_PLACEHOLDER: 'Geben oder wählen Sie eine Option aus',
ADD_NEW_LINE_INSTR: 'Drücken Sie UMSCHALT + EINGABETASTE, um eine neue Zeile hinzuzufügen',
ERROR: 'Fehler',
LOADING_LABEL: 'Laden',
WAIT_LABEL: 'Bitte warten',
FORM_404_HEADER: '404 - Formular existiert nicht',
FORM_404_BODY: 'Das Formular, auf das Sie zugreifen möchten, existiert nicht. Das tut mir leid!',
FORM_UNAUTHORIZED_HEADER: 'Nicht zum Zugriffsformular berechtigt\' ',
   FORM_UNAUTHORIZED_BODY1: 'Das Formular, auf das Sie zugreifen möchten, ist derzeit privat und nicht öffentlich zugänglich.',
   FORM_UNAUTHORIZED_BODY2: 'Wenn Sie der Eigentümer des Formulars sind, können Sie es im Fenster "Konfiguration" im Formular admin auf "Öffentlich" setzen.',
   FORM_UNAUTHORIZED_BODY1: 'Das Formular, auf das Sie zugreifen möchten, ist derzeit privat und nicht öffentlich zugänglich.',
   FORM_UNAUTHORIZED_BODY2: 'Wenn Sie der Eigentümer des Formulars sind, können Sie es im Fenster "Konfiguration" im Formular admin auf "Öffentlich" setzen.',
});
}]);

View File

@ -33,6 +33,9 @@ angular.module('view-form').config(['$translateProvider', function ($translatePr
OPTION_PLACEHOLDER: 'Digitare o selezionare un\'opzione',
ADD_NEW_LINE_INSTR: 'Premere SHIFT + INVIO per aggiungere una nuova riga',
ERROR: 'Errore',
LOADING_LABEL: 'Caricamento',
WAIT_LABEL: "Attendere prego",
FORM_404_HEADER: '404 - Il modulo non esiste',
FORM_404_BODY: 'La forma che stai cercando di accedere non esiste. Ci dispiace!',

View File

@ -34,6 +34,9 @@ angular.module('view-form').config(['$translateProvider', function ($translatePr
ADD_NEW_LINE_INSTR: 'Presione MAYÚS + ENTRAR para agregar una nueva línea',
ERROR: 'Error',
LOADING_LABEL: 'Cargando',
WAIT_LABEL: 'Espera',
FORM_404_HEADER: '404 - La forma no existe',
FORM_404_BODY: 'El formulario al que intenta acceder no existe. ¡Lo siento por eso!',

View File

@ -46,7 +46,8 @@
</field-directive>
</div>
<div class="row form-actions" id="submit_field" du-scrollspy="submit_field"
ng-style="{ 'background-color':myform.design.colors.buttonColor}" style="border-top: 1px solid #ddd; margin-top: 30vh; height: 100vh; margin-left: 1%; margin-right: 1%;"
ng-style="{ 'background-color':myform.design.colors.buttonColor}"
style="border-top: 1px solid #ddd; margin-top: 30vh; height: 100vh; margin-left: 1%; margin-right: 1%;"
on-tab-and-shift-key="prevField()"
on-tab-key="nextField()"
on-enter-key="submitForm()">
@ -55,7 +56,10 @@
{{ 'COMPLETING_NEEDED' | translate:translateAdvancementData }}
</div>
<button ng-if="!forms.myForm.$invalid" class="Button btn col-sm-2 col-xs-8 focusOn" v-busy="loading" v-busy-label="Please wait" v-pressable ng-disabled="loading || forms.myForm.$invalid" ng-click="submitForm()" on-enter-key-disabled="loading || forms.myForm.$invalid" ng-style="{'background-color':myform.design.colors.buttonColor, 'color':myform.design.colors.buttonTextColor}" style="font-size: 1.6em; margin-left: 1em; margin-top: 1em;">
<button ng-if="!forms.myForm.$invalid" class="Button btn col-sm-2 col-xs-8 focusOn" v-busy="loading" v-busy-label="{{ 'WAIT_LABEL' | translate }}" v-pressable
ng-disabled="loading || forms.myForm.$invalid" ng-click="submitForm()" on-enter-key-disabled="loading || forms.myForm.$invalid"
ng-style="{'background-color':myform.design.colors.buttonColor, 'color':myform.design.colors.buttonTextColor}"
style="font-size: 1.6em; margin-left: 1em; margin-top: 1em;">
{{ 'SUBMIT' | translate }}
</button>

View File

@ -47,9 +47,5 @@
$scope: scope
});
}));
it('should expose the authentication service', function() {
//expect(scope.authentication).toBeTruthy();
});
});
})();

View File

@ -65,6 +65,9 @@ angular.module('forms').config(['$translateProvider', function ($translateProvid
VIEW_MY_TELLFORM: 'View my tellform',
LIVE: 'Live',
PREVIEW: 'Preview',
//Share Tab
COPIED_LABEL: 'Copied',
COPY: 'Copy',
COPY_AND_PASTE: 'Copy and Paste this to add your TellForm to your website',
CHANGE_WIDTH_AND_HEIGHT: 'Change the width and height values to suit you best',

View File

@ -65,6 +65,9 @@ angular.module('forms').config(['$translateProvider', function ($translateProvid
VIEW_MY_TELLFORM: "Afficher ma forme",
LIVE: "Live",
PREVIEW: 'Aperçu',
//Share Tab
COPIED_LABEL: 'Copié',
COPY: "Copier",
COPY_AND_PASTE: "Copiez et collez ceci pour ajouter votre TellForm à votre site Web",
CHANGE_WIDTH_AND_HEIGHT: "Changez les valeurs de largeur et de hauteur pour mieux vous convenir",

View File

@ -65,9 +65,11 @@ angular.module('forms').config(['$translateProvider', function ($translateProvid
VIEW_MY_TELLFORM: 'Mein tellform anzeigen',
LIVE: 'Leben',
PREVIEW: 'Vorschau',
//Share Tab
COPIED_LABEL: 'Kopiert',
COPY: 'Kopieren',
COPY_AND_PASTE: 'Kopieren und einfügen, um Ihre TellForm auf Ihrer Website hinzuzufügen',
CHANGE_WIDTH_AND_HEIGHT: 'Ändern Sie die Werte für Breite und Höhe, um Ihnen am besten zu entsprechen',
POWERED_BY: 'Unterstützt von',
TELLFORM_URL: "Ihr TellForm ist dauerhaft unter dieser URL",

View File

@ -65,9 +65,11 @@ angular.module('forms').config(['$translateProvider', function ($translateProvid
VIEW_MY_TELLFORM: 'Visualizza la mia informazione',
LIVE: 'Live',
PREVIEW: 'Anteprima',
// Share Tab
COPIED_LABEL: "Copiato",
COPY: 'Copia',
COPY_AND_PASTE: 'Copia e incolla questo per aggiungere il tuo TellForm al tuo sito web',
CHANGE_WIDTH_AND_HEIGHT: 'Modifica i valori di larghezza e di altezza per adattarti al meglio',
POWERED_BY: 'Offerto da',
TELLFORM_URL: 'Il tuo TellForm è permanente in questo URL',

View File

@ -65,9 +65,11 @@ angular.module('forms').config(['$translateProvider', function ($translateProvid
VIEW_MY_TELLFORM: 'Ver mi tellform',
LIVE: 'Online',
PREVIEW: 'Vista previa',
// Share Tab
COPIED_LABEL: 'Copiado',
COPY: 'Copiar',
COPY_AND_PASTE: 'Copiar y pegar esto para agregar su TellForm a su sitio web',
CHANGE_WIDTH_AND_HEIGHT: 'Cambie los valores de ancho y altura para adaptar el formulario a sus necesidades',
POWERED_BY: 'Con la tecnlogía de',
TELLFORM_URL: 'Tu TellForm está en esta URL permanente',

View File

@ -207,22 +207,11 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$windo
if(dataToSend.analytics && dataToSend.analytics.visitors){
delete dataToSend.analytics.visitors;
}
if(dataToSend.submissions){
delete dataToSend.submissions;
}
if(dataToSend.visible_form_fields){
delete dataToSend.visible_form_fields;
}
if(dataToSend.analytics){
delete dataToSend.analytics.visitors;
delete dataToSend.analytics.fields;
delete dataToSend.analytics.submissions;
delete dataToSend.analytics.views;
delete dataToSend.analytics.conversionRate;
}
delete dataToSend.created;
delete dataToSend.lastModified;
delete dataToSend.__v;

View File

@ -12,14 +12,13 @@ angular.module('forms').directive('configureFormDirective', ['$rootScope', '$sta
$rootScope.myform = $scope.myform;
$scope.languages = $rootScope.languages;
$scope.resetForm = $rootScope.resetForm;
$scope.update = $rootScope.update;
Quill.register('modules/placeholder', PlaceholderModule.default(Quill))
$scope.customModules = {
placeholder: {
placeholders: $scope.myform.visible_form_fields.map(function(field){
return {
id: field.globalId,
id: field.id,
label: field.title
};
}),
@ -39,19 +38,16 @@ angular.module('forms').directive('configureFormDirective', ['$rootScope', '$sta
{
heading: $translate.instant('GENERAL_TAB'),
route: 'viewForm.configure.general',
template: 'modules/forms/admin/views/adminTabs/configureTabs/general.html',
active: false
},
{
heading: $translate.instant('SELF_NOTIFICATIONS_TAB'),
route: 'viewForm.configure.self_notifications',
template: 'modules/forms/admin/views/adminTabs/configureTabs/self_notifications.html',
active: false
},
{
heading: $translate.instant('RESPONDENT_NOTIFICATIONS_TAB'),
route: 'viewForm.configure.respondent_notifications',
template: 'modules/forms/admin/views/adminTabs/configureTabs/respondent_notifications.html',
active: false
}
];
@ -78,6 +74,14 @@ angular.module('forms').directive('configureFormDirective', ['$rootScope', '$sta
}
}, 500);
});
$scope.saveInProgress = false;
$scope.saveChanges = function(){
$scope.saveInProgress = true;
$rootScope.update(false, $scope.myform, false, false, function(){
$scope.saveInProgress = false;
});
};
}
};
}

View File

@ -25,7 +25,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
items: '.sortable-fields',
forceHelperSize: true,
forcePlaceholderSize: true,
update: function(e, ui) {
stop: function(e, ui) {
$scope.update(false, $scope.myform, true, false, function(err){
if(err){
console.error(err);

View File

@ -74,6 +74,8 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
if(deviceStatData && deviceStatData.length){
for(var i=0; i<deviceStatData.length; i++){
var currDevice = deviceStatData[i];
//_id here is deviceType of field due to aggregation in getVisitorData
if(stats[currDevice._id]){
stats[currDevice._id] = currDevice;
}

View File

@ -3,10 +3,10 @@
angular.module('forms').directive('fieldIconDirective', function() {
return {
template: '<i ng-class="iconTypeMap[typeName]"></i>',
template: '<i class="{{iconTypeMap[typeName]}}"></i>',
restrict: 'E',
scope: {
typeName: '@'
typeName: '='
},
controller: function($scope){
$scope.iconTypeMap = {

View File

@ -11,7 +11,12 @@ angular.module('forms').directive('shareFormDirective', ['$rootScope', '$transla
controller: function($scope){
$scope.actualFormURL = $scope.actualformurl;
$scope.fullScreen = "<iframe id='iframe' src='" + $scope.actualFormURL + "' style='width:100%;height:500px;'></iframe>"+
$scope.isCopied = false;
$scope.onCopySuccess = function(){
$scope.isCopied = true;
}
$scope.embedCode = "<iframe id='iframe' src='" + $scope.actualFormURL + "' style='width:100%;height:500px;'></iframe>"+
"<div style='font-family: Sans-Serif;font-size: 12px;color: #999;opacity: 0.5; padding-top: 5px;'>"+
$translate.instant('POWERED_BY')+
"<a href='https://www.tellform.com' style='color: #999' target='_blank'>TellForm</a>"+
@ -32,6 +37,7 @@ angular.module('forms').directive('shareFormDirective', ['$rootScope', '$transla
];
$scope.go = function(tab){
$scope.isCopied = false;
tab.active = true;
$state.go(tab.route);
};

View File

@ -22,9 +22,7 @@
ng-model="myform.respondentNotifications.enabled"
switch-on-text="{{ 'ON' | translate }}"
switch-off-text="{{ 'OFF' | translate }}"
switch-active="{{formHasEmailField}}"
ng-true-value="true"
ng-false-value="false">
switch-active="{{formHasEmailField}}">
</div>
</div>
@ -38,7 +36,7 @@
<ui-select-match placeholder="{{ 'OPTION_PLACEHOLDER' | translate }}">
{{$select.selected.title}}
</ui-select-match>
<ui-select-choices repeat="field.globalId as field in emailFields | filter: { title: $select.search }">
<ui-select-choices repeat="field.id as field in emailFields | filter: { title: $select.search }">
<span ng-bind-html="field.title | highlight: $select.search">
</span>
</ui-select-choices>

View File

@ -42,7 +42,7 @@
<ui-select-match placeholder="{{ 'OPTION_PLACEHOLDER' | translate }}">
{{$select.selected.title}}
</ui-select-match>
<ui-select-choices repeat="field.globalId as field in emailFields | filter: { title: $select.search }">
<ui-select-choices repeat="field.id as field in emailFields | filter: { title: $select.search }">
<span ng-bind-html="field.title | highlight: $select.search">
</span>
</ui-select-choices>

View File

@ -5,15 +5,23 @@
</div>
<div class="row">
<div class="col-sm-10 form-input">
<span ngclipboard data-clipboard-target="#copyEmbedded">
<span>
<div id="copyEmbedded" class="form-control">
{{ fullScreen }}
{{ embedCode }}
</div>
</span>
</div>
<div class="col-sm-2">
<button class="btn btn-secondary view-form-btn" ngclipboard data-clipboard-target="#copyEmbedded">
{{ 'COPY' | translate }} <i class="fa fa-clipboard" aria-hidden="true"></i>
<button class="btn btn-secondary view-form-btn"
ngclipboard data-clipboard-text="{{embedCode}}" ngclipboard-success="onCopySuccess()">
<span ng-if="!isCopied">
{{ 'COPY' | translate }} <i class="fa fa-clipboard" aria-hidden="true"></i>
</span>
<span ng-if="isCopied">
{{ 'COPIED_LABEL' | translate }}
</span>
</button>
</div>
</div>

View File

@ -5,13 +5,19 @@
</div>
<div class="row">
<div class="col-sm-10 form-input">
<span ngclipboard data-clipboard-target="#copyURL">
<span>
<input id="copyURL" ng-value="actualFormURL" class="form-control ng-pristine ng-untouched ng-valid">
</span>
</div>
<div class="col-sm-2">
<button class="btn btn btn-secondary view-form-btn" ngclipboard data-clipboard-target="#copyURL">
{{ 'COPY' | translate }} <i class="fa fa-clipboard" aria-hidden="true"></i>
<button class="btn btn-secondary view-form-btn"
ngclipboard data-clipboard-target="#copyURL" ngclipboard-success="onCopySuccess()">
<span ng-if="!isCopied">
{{ 'COPY' | translate }} <i class="fa fa-clipboard" aria-hidden="true"></i>
</span>
<span ng-if="isCopied">
{{ 'COPIED_LABEL' | translate }}
</span>
</button>
</div>
</div>

View File

@ -9,7 +9,13 @@
<div class="row">
<div class="col-sm-offset-4 col-sm-2">
<button class="btn btn-signup btn-rounded" type="button" ng-click="update(false, myform, false, false, null)"><i class="icon-arrow-left icon-white"></i>{{ 'SAVE_CHANGES' | translate }}</button>
<button class="btn btn-signup btn-rounded" v-busy="saveInProgress"
v-busy-label="{{ 'WAIT_LABEL' | translate }}"
v-busy-text="{{ 'SAVE_CHANGES' | translate }}"
v-pressable ng-click="saveChanges()">
<i class="icon-arrow-left icon-white"></i>
</button>
</div>
<div class="col-sm-1">
<button class="btn btn-secondary btn-rounded" type="button" ng-click="resetForm()"><i class="icon-eye-open icon-white"></i>{{ 'CANCEL' | translate }}</button>

View File

@ -492,7 +492,7 @@
<div class="panel panel-default" style="background-color: #f5f5f5;">
<div class="panel-heading" ng-click="addNewField(type.name)" style="cursor: pointer; font-size:12px; padding-left: 10px; padding-right: 10px;">
<span>
<field-icon-directive type-name="{{type.name}}">
<field-icon-directive type-name="type.name">
</field-icon-directive>
</span>
@ -533,7 +533,7 @@
<div class="panel-heading">
<div class="row">
<span class="col-xs-1" ng-switch="field.fieldType">
<field-icon-directive type-name="{{field.fieldType}}">
<field-icon-directive type-name="field.fieldType">
</field-icon-directive>
</span>
<span class="col-xs-11">

View File

@ -19,32 +19,6 @@ angular.module('forms').run(['Menus',
return function(seconds) {
return new Date(0).setSeconds(seconds);
};
}]).filter('formValidity', [function(){
return function(formObj){
if(formObj && formObj.form_fields && formObj.visible_form_fields){
//get keys
var formKeys = Object.keys(formObj);
//we only care about things that don't start with $
var fieldKeys = formKeys.filter(function(key){
return key[0] !== '$';
});
var fields = formObj.form_fields;
var valid_count = fields.filter(function(field){
if(typeof field === 'object' && field.fieldType !== 'statement' && field.fieldType !== 'rating'){
return !!(field.fieldValue);
} else if(field.fieldType === 'rating'){
return true;
}
}).length;
return valid_count - (formObj.form_fields.length - formObj.visible_form_fields.length);
}
return 0;
};
}]).filter('trustSrc', ['$sce', function($sce){
return function(formUrl){
return $sce.trustAsResourceUrl(formUrl);

View File

@ -138,9 +138,6 @@
},
isAuthenticated: function() {
return true;
},
getUserState: function() {
return true;
}
};
});

View File

@ -30,7 +30,8 @@
{fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false},
{fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false}
],
_id: '525a8422f6d0f87f0e407a33'
_id: '525a8422f6d0f87f0e407a33',
submissionNum: 0
},{
title: 'Form Title2',
admin: '39223933b1f1dea0ce12fab9',
@ -40,7 +41,8 @@
{fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false},
{fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false}
],
_id: '52f6d0f87f5a407a384220e3'
_id: '52f6d0f87f5a407a384220e3',
submissionNum: 0
},{
title: 'Form Title3',
admin: '2fab9ed873937f0e1dea0ce1',
@ -50,7 +52,8 @@
{fieldType:'checkbox', title:'nascar', fieldValue: '', deletePreserved: false},
{fieldType:'checkbox', title:'hockey', fieldValue: '', deletePreserved: false}
],
_id: '922f6d0f87fed8730e4e1233'
_id: '922f6d0f87fed8730e4e1233',
submissionNum: 0
}
];

View File

@ -26,15 +26,11 @@
{fieldType:'checkbox', title:'nascar', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed83b0ce121f17393deafab9'},
{fieldType:'checkbox', title:'hockey', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed8317393deab0ce121ffab9'}
],
pdf: {},
pdfFieldMap: {},
startPage: {
showStart: false
},
showFooter: false,
isGenerated: false,
isLive: false,
autofillPDFs: false,
_id: '525a8422f6d0f87f0e407a33'
};

View File

@ -60,7 +60,6 @@
showEnd: false
},
showFooter: false,
isGenerated: false,
isLive: false,
_id: '525a8422f6d0f87f0e407a33'
};

View File

@ -26,14 +26,11 @@
{fieldType:'checkbox', title:'nascar', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed83b0ce121f17393deafab9'},
{fieldType:'checkbox', title:'hockey', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed8317393deab0ce121ffab9'}
],
pdf: {},
pdfFieldMap: {},
startPage: {
showStart: false,
buttons: []
},
showFooter: false,
isGenerated: false,
isLive: false,
_id: '525a8422f6d0f87f0e407a33'
};

View File

@ -46,16 +46,13 @@ var MobileDetect = function(userAgentStr){
visible_form_fields: [
{fieldType:'textfield', title:'First Name', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed873933b0ce121f1deafab9'},
{fieldType:'checkbox', title:'nascar', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed83b0ce121f17393deafab9'},
{fieldType:'checkbox', title:'hockey', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed8317393deab0ce121ffab9'} ],
pdf: {},
pdfFieldMap: {},
{fieldType:'checkbox', title:'hockey', fieldOptions: [], fieldValue: '', required: true, disabled: false, deletePreserved: false, _id: 'ed8317393deab0ce121ffab9'}
],
startPage: {
showStart: false
},
showFooter: false,
isGenerated: false,
isLive: false,
autofillPDFs: false,
_id: '525a8422f6d0f87f0e407a33'
};

View File

@ -47,10 +47,6 @@ angular.module('users').factory('Auth', ['$window', '$q', 'User',
return !!this._currentUser && this._currentUser.username;
},
getUserState: function() {
return userState;
},
login: function(new_user) {
userState.isLoggedIn = true;
this._currentUser = new_user;

View File

@ -133,9 +133,6 @@
},
isAuthenticated: function() {
return true;
},
getUserState: function() {
return true;
}
};
});

View File

@ -61,23 +61,8 @@
expect($window.user).toEqual(null);
expect(Auth.currentUser).toEqual(null);
expect(Auth.isAuthenticated()).toBe(false);
expect(Auth.getUserState().isLoggedIn).toBe(false);
}));
it('Auth.getUserState() should fetch current user state', function() {
//Run Service Logic to Test
Auth.login(sampleUser);
var currUserState = Auth.getUserState();
expect(currUserState.isLoggedIn).toBe(true);
//Run Service Logic to Test
Auth.logout();
currUserState = Auth.getUserState();
expect(currUserState.isLoggedIn).toBe(false);
});
it('Auth.ensureHasCurrentUser() should fetch most current user if it exists in $window, currentUser or fetch it from /users/me', function() {
//Run Service Logic to Test
Auth.ensureHasCurrentUser().then(function onSuccess(currUser){