fixed setup script
This commit is contained in:
commit
6e1a8960f2
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@ scripts/test_oscarhost.js
|
|||
scripts/oscarhost/private/
|
||||
coverage/
|
||||
uploads/
|
||||
app/e2e_tests/screeshots/*
|
||||
|
||||
# iOS / Apple
|
||||
# ===========
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
TellForm
|
||||
[<img src="/" width="250px">](https://digitalocean.com/)
|
||||
========
|
||||
|
||||
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UY555MCBZM722)
|
||||
|
@ -14,8 +14,8 @@ TellForm is an *opensource alternative to TypeForm* built ontop of nodejs that c
|
|||
[See examples here](https://tellform.com/examples)
|
||||
|
||||
####Sponsored by
|
||||
[<img src="https://upload.wikimedia.org/wikipedia/en/thumb/f/ff/DigitalOcean_logo.svg/1024px-DigitalOcean_logo.svg.png" width="250px">](https://digitalocean.com/)
|
||||
[<img src="https://raw.githubusercontent.com/docker-library/docs/831b07a52f9ff6577c915afc41af8158725829f4/sentry/logo.png" width="250px">](https://getsentry.com/)
|
||||
[<img src="https://www.digitalocean.com/assets/media/logos-badges/png/DO_Logo_Horizontal_Blue-3db19536.png" width="250px">](https://digitalocean.com/)
|
||||
[<img src="https://a0wx592cvgzripj.global.ssl.fastly.net/_static/780f0361d74cc3da6680cfa4f855336a/getsentry/images/branding/png/sentry-horizontal-black.png" width="250px">](https://getsentry.com/)
|
||||
[<img src="https://dka575ofm4ao0.cloudfront.net/assets/base/logos/common-aececb0b4319b8fb61ac5b47a6983f96.png" width="250px">](https://statuspage.io/)
|
||||
[<img src="http://bcsrq.com/wp-content/uploads/2014/04/StickerMuleLogo300.png" width="250px">](https://stickermule.com/)
|
||||
[<img src="https://app.sparkpost.com/assets/images/sparkpost-logo-color.svg" width="250px">](https://sparkpost.com/)
|
||||
|
@ -50,6 +50,8 @@ Before you start, make sure you have
|
|||
1. Redis installed and running at 127.0.0.1:6379
|
||||
2. MongoDB installed and running at 127.0.0.1:27017 (OR specify the host and port in config/env/all)
|
||||
|
||||
Also make sure to install DNS Masq or equivalent if running it locally on your computer (look at dns_masq_setup_osx for instructions on OSX)
|
||||
|
||||
Install dependencies first.
|
||||
```bash
|
||||
$ npm install
|
||||
|
|
|
@ -13,11 +13,19 @@ exports.index = function(req, res) {
|
|||
};
|
||||
|
||||
exports.form = function(req, res) {
|
||||
//Allow form to be embeded
|
||||
res.removeHeader('X-Frame-Options');
|
||||
//Allow form to be embedded
|
||||
res.set('X-Frame-Options', 'GOFORIT');
|
||||
|
||||
res.render('form', {
|
||||
user: req.user || null,
|
||||
request: req
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
exports.redoc = function(req, res) {
|
||||
res.render('redoc', {
|
||||
request: req
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ var mongoose = require('mongoose'),
|
|||
fs = require('fs-extra'),
|
||||
async = require('async'),
|
||||
path = require('path'),
|
||||
diff = require('deep-diff'),
|
||||
_ = require('lodash');
|
||||
|
||||
/**
|
||||
|
@ -69,74 +70,6 @@ exports.uploadPDF = function(req, res, next) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Upload PDF
|
||||
*/
|
||||
/*
|
||||
exports.uploadSubmissionFile = function(req, res, next) {
|
||||
|
||||
console.log('inside uploadPDF');
|
||||
|
||||
// console.log('\n\nProperty Descriptor\n-----------');
|
||||
// console.log(Object.getOwnPropertyDescriptor(req.files.file, 'path'));
|
||||
|
||||
console.log(req.files);
|
||||
|
||||
if(req.files){
|
||||
var file, _user, _path;
|
||||
|
||||
for(var i=0; i<req.files.length; i++){
|
||||
file = req.files[i];
|
||||
_user = req.user;
|
||||
_path = file.path;
|
||||
|
||||
|
||||
if (file.size === 0) {
|
||||
return next(new Error('File uploaded is EMPTY'));
|
||||
}else if(file.size > 100000000){
|
||||
return next(new Error('File uploaded exceeds MAX SIZE of 100MB'));
|
||||
}else {
|
||||
fs.exists(_path, function(exists) {
|
||||
|
||||
//If file exists move to user's form directory
|
||||
if(exists) {
|
||||
var newDestination = config.tmpUploadPath+_user.username;
|
||||
var stat = null;
|
||||
try {
|
||||
stat = fs.statSync(newDestination);
|
||||
} catch (err) {
|
||||
fs.mkdirSync(newDestination);
|
||||
}
|
||||
|
||||
if (stat && !stat.isDirectory()) {
|
||||
console.log('Directory cannot be created');
|
||||
return next(new Error('Directory cannot be created because an inode of a different type exists at "' + newDestination + '"'));
|
||||
}
|
||||
|
||||
console.log(path.join(newDestination, pdfFile.filename));
|
||||
|
||||
fs.move(pdfFile.path, path.join(newDestination, pdfFile.filename), function (err) {
|
||||
if (err) {
|
||||
return next(new Error(err.message));
|
||||
}
|
||||
pdfFile.path = path.join(newDestination, pdfFile.filename);
|
||||
console.log(pdfFile.filename + ' uploaded to ' + pdfFile.path);
|
||||
res.json(pdfFile);
|
||||
});
|
||||
|
||||
} else {
|
||||
return next(new Error('Did NOT get your file!'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
return next(new Error('Uploaded files were NOT detected'));
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* Delete a forms submissions
|
||||
*/
|
||||
|
@ -185,8 +118,6 @@ exports.createSubmission = function(req, res) {
|
|||
percentageComplete: req.body.percentageComplete
|
||||
});
|
||||
|
||||
if(!!form.plugins.oscarhost.baseUrl) submission.hasPlugins.oscarhost = true;
|
||||
|
||||
if(form.pdf) submission.pdf = form.pdf;
|
||||
|
||||
//Save submitter's IP Address
|
||||
|
@ -256,23 +187,27 @@ exports.listSubmissions = function(req, res) {
|
|||
* Create a new form
|
||||
*/
|
||||
exports.create = function(req, res) {
|
||||
|
||||
|
||||
if(!req.body.form){
|
||||
console.log(err);
|
||||
return res.status(400).send({
|
||||
message: "Invalid Input"
|
||||
});
|
||||
}
|
||||
var form = new Form(req.body.form);
|
||||
|
||||
form.admin = req.user._id;
|
||||
console.log('Create a new form');
|
||||
console.log(form);
|
||||
console.log(req.body.form);
|
||||
console.log(req.user);
|
||||
|
||||
form.save(function(err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
res.status(400).send({
|
||||
return res.status(405).send({
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
res.json(form);
|
||||
}
|
||||
|
||||
res.json(form);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -280,10 +215,7 @@ exports.create = function(req, res) {
|
|||
* Show the current form
|
||||
*/
|
||||
exports.read = function(req, res) {
|
||||
var validUpdateTypes= Form.schema.path('plugins.oscarhost.settings.updateType').enumValues;
|
||||
|
||||
var newForm = req.form.toJSON({virtuals : true});
|
||||
newForm.plugins.oscarhost.settings.validUpdateTypes = validUpdateTypes;
|
||||
|
||||
if (req.userId) {
|
||||
if(req.form.admin._id+'' === req.userId+''){
|
||||
|
@ -294,7 +226,6 @@ exports.read = function(req, res) {
|
|||
});
|
||||
}
|
||||
return res.json(newForm);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -302,27 +233,33 @@ exports.read = function(req, res) {
|
|||
*/
|
||||
exports.update = function(req, res) {
|
||||
var form = req.form;
|
||||
delete req.body.form.__v;
|
||||
delete req.body.form._id;
|
||||
|
||||
//Unless we have 'admin' priviledges, updating form admin is disabled
|
||||
if(req.user.roles.indexOf('admin') === -1) delete req.body.form.admin;
|
||||
if(req.body.changes){
|
||||
console.log('SENDING DIFFS\n\n\n');
|
||||
var formChanges = req.body.changes;
|
||||
|
||||
//Do this so we can create duplicate fields
|
||||
var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$');
|
||||
for(var i=0; i<req.body.form.form_fields.length; i++){
|
||||
var field = req.body.form.form_fields[i];
|
||||
if(!checkForValidId.exec(field._id+'')){
|
||||
delete field._id;
|
||||
formChanges.forEach(function (change) {
|
||||
diff.applyChange(form, true, change);
|
||||
});
|
||||
} else {
|
||||
//Unless we have 'admin' privileges, updating form admin is disabled
|
||||
if(req.user.roles.indexOf('admin') === -1) delete req.body.form.admin;
|
||||
|
||||
//Do this so we can create duplicate fields
|
||||
var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$');
|
||||
for(var i=0; i<req.body.form.form_fields.length; i++){
|
||||
var field = req.body.form.form_fields[i];
|
||||
if(!checkForValidId.exec(field._id+'')){
|
||||
delete field._id;
|
||||
}
|
||||
}
|
||||
form = _.extend(form, req.body.form);
|
||||
}
|
||||
|
||||
form = _.extend(form, req.body.form);
|
||||
|
||||
form.save(function(err, form) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
res.status(400).send({
|
||||
res.status(405).send({
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
|
@ -382,7 +319,7 @@ exports.formByID = function(req, res, next, id) {
|
|||
if (err) {
|
||||
return next(err);
|
||||
} else if (form === undefined || form === null) {
|
||||
res.status(400).send({
|
||||
res.status(404).send({
|
||||
message: 'Form not found'
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ var _ = require('lodash'),
|
|||
config = require('../../../config/config'),
|
||||
nodemailer = require('nodemailer'),
|
||||
crypto = require('crypto'),
|
||||
User = mongoose.model('User');
|
||||
User = mongoose.model('User'),
|
||||
tokgen = require("../../libs/tokenGenerator");
|
||||
|
||||
|
||||
var nev = require('email-verification')(mongoose);
|
||||
|
||||
|
@ -99,6 +101,7 @@ exports.resendVerificationEmail = function(req, res, next){
|
|||
* Signup
|
||||
*/
|
||||
exports.signup = function(req, res) {
|
||||
debugger;
|
||||
|
||||
// For security measures we remove the roles from the req.body object
|
||||
delete req.body.roles;
|
||||
|
@ -172,7 +175,6 @@ exports.signin = function(req, res, next) {
|
|||
*/
|
||||
exports.signout = function(req, res) {
|
||||
req.logout();
|
||||
//res.redirect('/');
|
||||
return res.status(200).send('You have successfully logged out.');
|
||||
|
||||
};
|
||||
|
@ -304,3 +306,43 @@ exports.removeOAuthProvider = function(req, res, next) {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* Generate API Key for User */
|
||||
exports.generateAPIKey = function(req, res) {
|
||||
if (!req.isAuthenticated()){
|
||||
return res.status(400).send({
|
||||
message: 'User is not Authorized'
|
||||
});
|
||||
}
|
||||
|
||||
User.findById(req.user.id)
|
||||
.exec( function(err, user) {
|
||||
if (err) return res.status(400).send(err);
|
||||
|
||||
if (!user) {
|
||||
return res.status(400).send({
|
||||
message: 'User does not Exist'
|
||||
});
|
||||
}
|
||||
|
||||
user.apiKey = tokgen();
|
||||
|
||||
user.save(function(err, _user) {
|
||||
if (err) {
|
||||
return res.status(400).send({
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
}
|
||||
|
||||
var newUser = _user.toObject();
|
||||
delete newUser.salt;
|
||||
delete newUser.__v;
|
||||
delete newUser.passwordHash;
|
||||
delete newUser.provider;
|
||||
|
||||
console.log(newUser);
|
||||
return res.json(newUser);
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
|
|
@ -23,7 +23,9 @@ exports.userByID = function (req, res, next, id) {
|
|||
if (err) {
|
||||
return next(err);
|
||||
} else if (!user) {
|
||||
return next(new Error('Failed to load User ' + id));
|
||||
return res.status(404).send({
|
||||
message: 'User does not exist'
|
||||
});
|
||||
}
|
||||
|
||||
req.profile = user;
|
||||
|
|
|
@ -27,13 +27,13 @@ exports.update = function(req, res) {
|
|||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return res.status(400).send({
|
||||
return res.status(500).send({
|
||||
message: errorHandler.getErrorMessage(err)
|
||||
});
|
||||
} else {
|
||||
req.login(user, function(err) {
|
||||
if (err) {
|
||||
res.status(400).send(err);
|
||||
res.status(500).send(err);
|
||||
} else {
|
||||
res.json(user);
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ exports.update = function(req, res) {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
res.status(400).send({
|
||||
res.status(401).send({
|
||||
message: 'User is not signed in'
|
||||
});
|
||||
}
|
||||
|
|
8
app/libs/tokenGenerator.js
Normal file
8
app/libs/tokenGenerator.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
"use strict";
|
||||
|
||||
var TokenGenerator = require("uuid-token-generator");
|
||||
var tokgen = new TokenGenerator(256, TokenGenerator.BASE62);
|
||||
|
||||
module.exports = function() {
|
||||
return tokgen.generate();
|
||||
};
|
|
@ -92,11 +92,6 @@ var FormSchema = new Schema({
|
|||
default: 'en',
|
||||
required: 'Form must have a language'
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
analytics:{
|
||||
gaCode: {
|
||||
type: String
|
||||
|
@ -146,18 +141,10 @@ var FormSchema = new Schema({
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isGenerated: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isLive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
autofillPDFs: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
design: {
|
||||
colors:{
|
||||
|
@ -187,57 +174,7 @@ var FormSchema = new Schema({
|
|||
default: '#333'
|
||||
}
|
||||
},
|
||||
font: String,
|
||||
backgroundImage: { type: Schema.Types.Mixed }
|
||||
},
|
||||
|
||||
plugins: {
|
||||
oscarhost: {
|
||||
baseUrl: {
|
||||
type: String
|
||||
},
|
||||
settings: {
|
||||
lookupField: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Field'
|
||||
},
|
||||
updateType: {
|
||||
type: String,
|
||||
enum: ['upsert', 'force_add', 'force_update', 'fetch'],
|
||||
},
|
||||
fieldMap: {
|
||||
type: Schema.Types.Mixed,
|
||||
},
|
||||
validUpdateTypes: {
|
||||
type: [String]
|
||||
},
|
||||
validFields : {
|
||||
type: [String],
|
||||
default: [
|
||||
'address',
|
||||
'city',
|
||||
'email',
|
||||
'firstName',
|
||||
'hin',
|
||||
'lastName',
|
||||
'phone',
|
||||
'postal',
|
||||
'province',
|
||||
'sex',
|
||||
'spokenLanguage',
|
||||
'title',
|
||||
'DOB']
|
||||
}
|
||||
},
|
||||
auth: {
|
||||
user: {
|
||||
type: String
|
||||
},
|
||||
pass: {
|
||||
type: String
|
||||
}
|
||||
}
|
||||
}
|
||||
font: String
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -367,11 +304,6 @@ FormSchema.pre('save', function (next) {
|
|||
}
|
||||
});
|
||||
}, function(cb) {
|
||||
//DAVID: TODO: Make this so we don't have to update the validFields property ever save
|
||||
if (that.plugins.oscarhost.hasOwnProperty('baseUrl')) {
|
||||
var validUpdateTypes = mongoose.model('Form').schema.path('plugins.oscarhost.settings.updateType').enumValues;
|
||||
that.plugins.oscarhost.settings.validUpdateTypes = validUpdateTypes;
|
||||
}
|
||||
return cb(null);
|
||||
},
|
||||
function(cb) {
|
||||
|
@ -489,12 +421,18 @@ FormSchema.pre('save', function (next) {
|
|||
else return cb();
|
||||
},
|
||||
function(cb) {
|
||||
|
||||
if(that.isModified('form_fields') && that.form_fields && _original){
|
||||
var hasIds = true;
|
||||
for(var i=0; i<that.form_fields.length; i++){
|
||||
if(!that.form_fields.hasOwnProperty('_id')){
|
||||
hasIds = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(that.isModified('form_fields') && that.form_fields && _original && hasIds){
|
||||
|
||||
var old_form_fields = _original.form_fields,
|
||||
new_ids = _.map(_.pluck(that.form_fields, '_id'), function(id){ return ''+id;}),
|
||||
old_ids = _.map(_.pluck(old_form_fields, '_id'), function(id){ return ''+id;}),
|
||||
new_ids = _.map(_.pluck(that.form_fields, 'id'), function(id){ return ''+id;}),
|
||||
old_ids = _.map(_.pluck(old_form_fields, 'id'), function(id){ return ''+id;}),
|
||||
deletedIds = getDeletedIndexes(old_ids, new_ids);
|
||||
|
||||
//Preserve fields that have at least one submission
|
||||
|
|
|
@ -7,7 +7,8 @@ var mongoose = require('mongoose'),
|
|||
util = require('util'),
|
||||
mUtilities = require('mongoose-utilities'),
|
||||
_ = require('lodash'),
|
||||
Schema = mongoose.Schema;
|
||||
Schema = mongoose.Schema,
|
||||
LogicJumpSchema = require('./logic_jump.server.model');
|
||||
|
||||
var FieldOptionSchema = new Schema({
|
||||
option_id: {
|
||||
|
@ -77,10 +78,7 @@ function BaseFieldSchema(){
|
|||
default: ''
|
||||
},
|
||||
|
||||
logicJump: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'LogicJump'
|
||||
},
|
||||
logicJump: LogicJumpSchema,
|
||||
|
||||
ratingOptions: {
|
||||
type: RatingFieldSchema,
|
||||
|
@ -162,6 +160,7 @@ FormFieldSchema.pre('validate', function(next) {
|
|||
|
||||
if(this.ratingOptions && this.ratingOptions.steps && this.ratingOptions.shape){
|
||||
error.errors.ratingOptions = new mongoose.Error.ValidatorError({path: 'ratingOptions', message: 'ratingOptions is only allowed for type \'rating\' fields.', type: 'notvalid', value: this.ratingOptions});
|
||||
console.error(error);
|
||||
return(next(error));
|
||||
}
|
||||
|
||||
|
@ -183,8 +182,9 @@ FormFieldSchema.pre('validate', function(next) {
|
|||
|
||||
//If field is multiple choice check that it has field
|
||||
if(this.fieldType !== 'dropdown' && this.fieldType !== 'radio' && this.fieldType !== 'checkbox'){
|
||||
if(!this.fieldOptions || this.fieldOptions.length !== 0){
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -192,13 +192,18 @@ FormFieldSchema.pre('validate', function(next) {
|
|||
return next();
|
||||
});
|
||||
|
||||
//LogicJump Save
|
||||
FormFieldSchema.pre('save', function(next) {
|
||||
if(this.logicJump && this.logicJump.fieldA){
|
||||
if(this.logicJump.jumpTo = '') delete this.logicJump.jumpTo;
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
//Submission fieldValue correction
|
||||
FormFieldSchema.pre('save', function(next) {
|
||||
|
||||
if(this.fieldType === 'dropdown' && this.isSubmission){
|
||||
//console.log(this);
|
||||
this.fieldValue = this.fieldValue.option_value;
|
||||
//console.log(this.fieldValue);
|
||||
}
|
||||
|
||||
return next();
|
||||
|
|
|
@ -158,88 +158,7 @@ FormSubmissionSchema.pre('save', function (next) {
|
|||
// console.log(_form);
|
||||
// console.log('should push to api');
|
||||
// console.log( (!this.oscarDemoNum && !!_form.plugins.oscarhost.baseUrl && !!_form.plugins.oscarhost.settings.fieldMap) );
|
||||
if (!this.oscarDemoNum && _form.plugins.oscarhost.baseUrl && _form.plugins.oscarhost.settings.fieldMap) {
|
||||
console.log('OSCARHOST API HOOK');
|
||||
var url_login = _form.plugins.oscarhost.baseUrl + '/LoginService?wsdl',
|
||||
url_demo = _form.plugins.oscarhost.baseUrl + '/DemographicService?wsdl';
|
||||
|
||||
var args_login = {arg0: config.oscarhost.auth.user, arg1: config.oscarhost.auth.pass};
|
||||
|
||||
var options = {
|
||||
ignoredNamespaces: {
|
||||
namespaces: ['targetNamespace', 'typedNamespace'],
|
||||
override: true
|
||||
}
|
||||
};
|
||||
// console.log(self.form_fields);
|
||||
|
||||
//Generate demographics from hashmap
|
||||
var generateDemo = function (formFields, conversionMap, demographicsTemplate) {
|
||||
console.log('generating Demo fields');
|
||||
console.log(conversionMap);
|
||||
var _generatedDemo = {}, currField, propertyName;
|
||||
|
||||
for (var y = 0; y < formFields.length; y++) {
|
||||
currField = formFields[y];
|
||||
propertyName = conversionMap[currField._id];
|
||||
|
||||
if (demographicsTemplate.hasOwnProperty(conversionMap[currField._id])) {
|
||||
_generatedDemo[propertyName] = currField.fieldValue + '';
|
||||
} else if (propertyName === 'DOB') {
|
||||
var date = new Date(currField.fieldValue);
|
||||
_generatedDemo.dateOfBirth = date.getDate() + '';
|
||||
_generatedDemo.yearOfBirth = date.getFullYear() + '';
|
||||
_generatedDemo.monthOfBirth = date.getMonth() + '';
|
||||
}
|
||||
}
|
||||
var currDate = new Date();
|
||||
var dateString = currDate.toISOString().split('T')[0] + ' ' + currDate.toISOString().split('T')[1].slice(0, 8);
|
||||
_generatedDemo.lastUpdateDate = currDate.toISOString();
|
||||
return _generatedDemo;
|
||||
};
|
||||
|
||||
var submissionDemographic = generateDemo(self.form_fields, _form.plugins.oscarhost.settings.fieldMap, newDemoTemplate);
|
||||
|
||||
console.log(submissionDemographic);
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
//Authenticate with API
|
||||
soap.createClient(url_login, options, function (err, client) {
|
||||
client.login(args_login, function (err, result) {
|
||||
if (err) return callback(err);
|
||||
console.log('SOAP authenticated');
|
||||
return callback(null, result.return);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
function (security_obj, callback) {
|
||||
//Force Add Demographic
|
||||
if (_form.plugins.oscarhost.settings.updateType === 'force_add') {
|
||||
soap.createClient(url_demo, options, function (err, client) {
|
||||
if (err) return callback(err);
|
||||
client.setSecurity(new OscarSecurity(security_obj.securityId, security_obj.securityTokenKey));
|
||||
|
||||
client.addDemographic({arg0: submissionDemographic}, function (err, result) {
|
||||
console.log('FORCE ADDING DEMOGRAPHIC \n');
|
||||
// console.log(result.return);
|
||||
if (err) return callback(err);
|
||||
return callback(null, result);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
], function (err, result) {
|
||||
if (err) return next(err);
|
||||
|
||||
self.oscarDemoNum = parseInt(result.return, 10);
|
||||
console.log('self.oscarDemoNum: ' + self.oscarDemoNum);
|
||||
return next();
|
||||
});
|
||||
} else {
|
||||
return next();
|
||||
}
|
||||
return next();
|
||||
});
|
||||
} else {
|
||||
return next();
|
||||
|
|
|
@ -6,76 +6,76 @@
|
|||
var mongoose = require('mongoose'),
|
||||
Schema = mongoose.Schema,
|
||||
_ = require('lodash'),
|
||||
math = require('math');
|
||||
math = require('mathjs');
|
||||
|
||||
|
||||
var BooleanExpressionSchema = new Schema({
|
||||
expressionString: {
|
||||
type: String,
|
||||
var schemaOptions = {
|
||||
toObject: {
|
||||
virtuals: true
|
||||
},
|
||||
result: {
|
||||
type: Boolean,
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
BooleanExpressionSchema.methods.evaluate = function(){
|
||||
if(this.expressionString){
|
||||
//Get headNode
|
||||
var headNode = math.parse(this.expressionString);
|
||||
var expressionScope = {};
|
||||
var that = this;
|
||||
|
||||
//Create scope
|
||||
headNode.traverse(function (node, path, parent) {
|
||||
if(node.type === 'SymbolNode'){
|
||||
|
||||
mongoose.model('Field')
|
||||
.findOne({_id: node.name}).exec(function(err, field){
|
||||
if(err) {
|
||||
console.log(err);
|
||||
throw new Error(err);
|
||||
}
|
||||
|
||||
if(!!_.parseInt(field.fieldValue)){
|
||||
that.expressionScope[node.name] = _.parseInt(field.fieldValue);
|
||||
}else {
|
||||
that.expressionScope[node.name] = field.fieldValue;
|
||||
}
|
||||
console.log('_id: '+node.name);
|
||||
console.log('value: '+that.expressionScope[node.name]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var code = headNode.compile();
|
||||
var result = code.eval(expressionScope);
|
||||
|
||||
this.result = result;
|
||||
return result;
|
||||
}else{
|
||||
return null;
|
||||
toJSON: {
|
||||
virtuals: true
|
||||
}
|
||||
};
|
||||
|
||||
mongoose.model('BooleanExpression', BooleanExpressionSchema);
|
||||
/**
|
||||
* Form Schema
|
||||
*/
|
||||
var LogicJumpSchema = new Schema({
|
||||
created: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
expressionString: {
|
||||
type: String,
|
||||
enum: [
|
||||
'field == static',
|
||||
'field != static',
|
||||
'field > static',
|
||||
'field >= static',
|
||||
'field <= static',
|
||||
'field < static',
|
||||
'field contains static',
|
||||
'field !contains static',
|
||||
'field begins static',
|
||||
'field !begins static',
|
||||
'field ends static',
|
||||
'field !ends static',
|
||||
]
|
||||
},
|
||||
lastModified: {
|
||||
type: Date,
|
||||
},
|
||||
|
||||
BooleanExpression: {
|
||||
fieldA: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'BooleanExpression'
|
||||
ref: 'FormField'
|
||||
},
|
||||
valueB: {
|
||||
type: Schema.Types.String
|
||||
},
|
||||
jumpTo: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'FormField'
|
||||
}
|
||||
}, schemaOptions);
|
||||
|
||||
});
|
||||
/*
|
||||
IS EQUAL TO statement
|
||||
|
||||
var scope = {
|
||||
a: val1,
|
||||
b: val2
|
||||
};
|
||||
|
||||
math.eval('a == b', scope);
|
||||
|
||||
IS NOT EQUAL TO statement
|
||||
var scope = {
|
||||
a: val1,
|
||||
b: val2
|
||||
};
|
||||
|
||||
math.eval('a !== b', scope);
|
||||
|
||||
BEGINS WITH statement
|
||||
|
||||
ENDS WITH statement
|
||||
|
||||
CONTAINS statement
|
||||
|
||||
DOES NOT CONTAIN statement
|
||||
|
||||
*/
|
||||
|
||||
mongoose.model('LogicJump', LogicJumpSchema);
|
||||
|
||||
module.exports = LogicJumpSchema;
|
||||
|
|
|
@ -75,6 +75,7 @@ var UserSchema = new Schema({
|
|||
type: String,
|
||||
unique: true,
|
||||
required: false,
|
||||
lowercase: true,
|
||||
trim: true
|
||||
},
|
||||
passwordHash: {
|
||||
|
@ -119,7 +120,13 @@ var UserSchema = new Schema({
|
|||
resetPasswordExpires: {
|
||||
type: Date
|
||||
},
|
||||
token: String
|
||||
token: String,
|
||||
apiKey: {
|
||||
type: String,
|
||||
unique: true,
|
||||
index: true,
|
||||
sparse: true
|
||||
},
|
||||
});
|
||||
|
||||
UserSchema.virtual('displayName').get(function () {
|
||||
|
|
|
@ -7,10 +7,11 @@ var forms = require('../../app/controllers/forms.server.controller'),
|
|||
core = require('../../app/controllers/core.server.controller');
|
||||
|
||||
module.exports = function(app) {
|
||||
// Root routing
|
||||
// Core routing
|
||||
app.route('/').get(core.index);
|
||||
app.route('/subdomain/([a-zA-Z0-9]+)/').get(core.form);
|
||||
app.route('/subdomain/*/forms/:formId([a-zA-Z0-9]+)')
|
||||
.get(forms.read)
|
||||
|
||||
app.route('/subdomain/api/').get(core.redoc);
|
||||
app.route('/subdomain/:userSlug((?!api$)[A-Za-z0-9]+)/').get(core.form);
|
||||
app.route('/subdomain/:userSlug((?!api$)[A-Za-z0-9]+)/forms/:formId([a-zA-Z0-9]+)').get(forms.read)
|
||||
.post(forms.createSubmission);
|
||||
};
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
var users = require('../../app/controllers/users.server.controller'),
|
||||
forms = require('../../app/controllers/forms.server.controller'),
|
||||
multer = require('multer'),
|
||||
config = require('../../config/config');
|
||||
config = require('../../config/config'),
|
||||
auth = require('../../config/passport_helpers');
|
||||
|
||||
// Setting the pdf upload route and folder
|
||||
var storage = multer.diskStorage({
|
||||
|
@ -27,21 +28,21 @@ var upload = multer({
|
|||
module.exports = function(app) {
|
||||
// Form Routes
|
||||
app.route('/upload/pdf')
|
||||
.post(users.requiresLogin, upload.single('file'), forms.uploadPDF);
|
||||
.post(auth.isAuthenticatedOrApiKey, upload.single('file'), forms.uploadPDF);
|
||||
|
||||
app.route('/forms')
|
||||
.get(users.requiresLogin, forms.list)
|
||||
.post(users.requiresLogin, forms.create);
|
||||
.get(auth.isAuthenticatedOrApiKey, forms.list)
|
||||
.post(auth.isAuthenticatedOrApiKey, forms.create);
|
||||
|
||||
app.route('/forms/:formId([a-zA-Z0-9]+)')
|
||||
.get(forms.read)
|
||||
.post(forms.createSubmission)
|
||||
.put(users.requiresLogin, forms.hasAuthorization, forms.update)
|
||||
.delete(users.requiresLogin, forms.hasAuthorization, forms.delete);
|
||||
.put(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.update)
|
||||
.delete(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.delete);
|
||||
|
||||
app.route('/forms/:formId([a-zA-Z0-9]+)/submissions')
|
||||
.get(users.requiresLogin, forms.hasAuthorization, forms.listSubmissions)
|
||||
.delete(users.requiresLogin, forms.hasAuthorization, forms.deleteSubmissions);
|
||||
.get(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.listSubmissions)
|
||||
.delete(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.deleteSubmissions);
|
||||
|
||||
// Finish by binding the form middleware
|
||||
app.param('formId', forms.formByID);
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var passport = require('passport');
|
||||
var config = require('../../config/config');
|
||||
var passport = require('passport'),
|
||||
config = require('../../config/config'),
|
||||
auth = require('../../config/passport_helpers');
|
||||
|
||||
module.exports = function(app) {
|
||||
// User Routes
|
||||
var users = require('../../app/controllers/users.server.controller');
|
||||
|
||||
// Setting up the users profile api
|
||||
app.route('/users/me').get(users.requiresLogin, users.getUser);
|
||||
app.route('/users').put(users.requiresLogin, users.update);
|
||||
app.route('/users/me').get(auth.isAuthenticatedOrApiKey, users.getUser);
|
||||
app.route('/users').put(auth.isAuthenticatedOrApiKey, users.update);
|
||||
app.route('/users/accounts').delete(users.requiresLogin, users.removeOAuthProvider);
|
||||
|
||||
// Setting up the users account verification api
|
||||
|
@ -32,6 +33,8 @@ module.exports = function(app) {
|
|||
app.route('/auth/signin').post(users.signin);
|
||||
app.route('/auth/signout').get(users.signout);
|
||||
|
||||
app.route('/auth/genkey').get(users.requiresLogin, users.generateAPIKey);
|
||||
|
||||
// // Setting the facebook oauth routes
|
||||
// app.route('/auth/facebook').get(passport.authenticate('facebook', {
|
||||
// scope: ['email']
|
||||
|
|
|
@ -55,12 +55,8 @@ module.exports = function (io, socket) {
|
|||
// a user has visited our page - add them to the visitorsData object
|
||||
socket.on('form-visitor-data', function(data) {
|
||||
|
||||
console.log('\n\nuser has visited our page');
|
||||
|
||||
visitorsData[socket.id] = data;
|
||||
|
||||
console.log(data);
|
||||
|
||||
if (data.isSubmitted) {
|
||||
saveVisitorData(data, function () {
|
||||
console.log('\n\n user submitted form');
|
||||
|
|
|
@ -148,13 +148,6 @@ describe('FormSubmission Model Unit Tests:', function() {
|
|||
beforeEach(function(done){
|
||||
|
||||
var myFieldMap = {};
|
||||
myFieldMap[myForm.form_fields[0]._id+''] = 'firstName';
|
||||
myFieldMap[myForm.form_fields[1]._id+''] = 'lastName';
|
||||
myFieldMap[myForm.form_fields[2]._id+''] = 'sex';
|
||||
myFieldMap[myForm.form_fields[3]._id+''] = 'DOB';
|
||||
myFieldMap[myForm.form_fields[4]._id+''] = 'phone';
|
||||
|
||||
myForm.plugins.oscarhost.settings.fieldMap = myFieldMap;
|
||||
|
||||
myForm.save(function(err, form){
|
||||
if(err) done(err);
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
// 'use strict';
|
||||
|
||||
// var should = require('should'),
|
||||
// mongoose = require('mongoose'),
|
||||
// math = require('math'),
|
||||
// Form = mongoose.model('Form'),
|
||||
// LogicJump = mongoose.model('LogicJump'),
|
||||
// BooleanƒExpression = mongoose.model('BooleanExpression'),
|
||||
// Field = mongoose.model('Field');
|
||||
|
||||
// /**
|
||||
// * Globals
|
||||
// */
|
||||
// var textField;
|
||||
|
||||
// /**
|
||||
// * Form routes tests
|
||||
// */
|
||||
// describe('LogicJump Tests', function() {
|
||||
|
||||
// beforeEach(function(done) {
|
||||
// textField = new Field({
|
||||
// title: 'First Name',
|
||||
// fieldType: 'textfield',
|
||||
// fieldValue: 'David',
|
||||
// });
|
||||
|
||||
// textField.save(function(err){
|
||||
// if(err) done(err);
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('BooleanExpression', function(){
|
||||
// var scope;
|
||||
|
||||
// beforeEach(function(){
|
||||
// scope = {
|
||||
// x: false,
|
||||
// y: true,
|
||||
// b: 5,
|
||||
// a: 3
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('should evaluate a simple boolean expression to be true', function(){
|
||||
// var expression = 'and( or( x, y), (b > a))';
|
||||
|
||||
// var code = math.parse(expression);
|
||||
// var actual = code.compile().eval(scope);
|
||||
|
||||
// // var expected = ( (false || true) && !!(5 > 3) );
|
||||
|
||||
// actual.should.equal(true);
|
||||
// });
|
||||
|
||||
|
||||
// afterEach(function(done){
|
||||
// BooleanExpression.remove().exec(function() {
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// afterEach(function(done) {
|
||||
// Field.remove().exec(function() {
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
// });
|
Binary file not shown.
|
@ -2,7 +2,7 @@
|
|||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title>{{title}}</title>
|
||||
<title>{{title}} Form</title>
|
||||
|
||||
<!-- General META -->
|
||||
<meta charset="utf-8">
|
||||
|
@ -61,11 +61,11 @@
|
|||
</head>
|
||||
|
||||
<body ng-cloak>
|
||||
<div class="github-fork-ribbon-wrapper right-bottom hidden-xs">
|
||||
<!--<div class="github-fork-ribbon-wrapper right-bottom hidden-xs">
|
||||
<div class="github-fork-ribbon">
|
||||
<a href="https://github.com/whitef0x0/tellform">Fork me on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>-->
|
||||
<section class="content">
|
||||
<section ui-view></section>
|
||||
</section>
|
||||
|
|
94
app/views/redoc.server.view.html
Normal file
94
app/views/redoc.server.view.html
Normal file
|
@ -0,0 +1,94 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title>{{title}}</title>
|
||||
|
||||
<!-- General META -->
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
<!-- Semantic META -->
|
||||
<meta name="keywords" content="{{keywords}}">
|
||||
<meta name="description" content="{{description}}">
|
||||
|
||||
<!-- Facebook META -->
|
||||
<meta property="og:site_name" content="{{title}}">
|
||||
<meta property="og:title" content="{{title}}">
|
||||
<meta property="og:description" content="{{description}}">
|
||||
<meta property="og:url" content="{{url}}">
|
||||
<meta property="og:image" content="/img/brand/logo.png">
|
||||
<meta property="og:type" content="website">
|
||||
|
||||
<!-- Twitter META -->
|
||||
<meta name="twitter:title" content="{{title}}">
|
||||
<meta name="twitter:description" content="{{description}}">
|
||||
<meta name="twitter:url" content="{{url}}">
|
||||
<meta name="twitter:image" content="/img/brand/logo.png">
|
||||
|
||||
<!-- Fav Icon -->
|
||||
<link href="/static/modules/core/img/brand/favicon.ico" rel="shortcut icon" type="image/x-icon">
|
||||
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,900'>
|
||||
|
||||
<!--Bower CSS dependencies-->
|
||||
|
||||
<!-- end Bower CSS dependencies-->
|
||||
|
||||
<!--Application CSS Files-->
|
||||
{% for cssFile in cssFiles %}
|
||||
<link rel="stylesheet" href="{{cssFile}}">
|
||||
{% endfor %}
|
||||
<!-- end Application CSS Files-->
|
||||
|
||||
|
||||
<!-- HTML5 Shim -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<redoc spec-url='/static/swagger.json'></redoc>
|
||||
<script src="https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js"> </script>
|
||||
|
||||
<!--Bower JS dependencies-->
|
||||
{% for bowerJSFile in bowerJSFiles %}
|
||||
<script type="text/javascript" src="{{bowerJSFile}}"></script>
|
||||
{% endfor %}
|
||||
<!-- end Bower JS dependencies-->
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-strap/2.3.8/angular-strap.min.js"></script>
|
||||
|
||||
{% if process.env.NODE_ENV === 'development' %}
|
||||
|
||||
<script src="https://cdn.ravenjs.com/2.3.0/angular/raven.min.js"></script>
|
||||
|
||||
<script>
|
||||
Raven.config('https://825fefd6b4ed4a4da199c1b832ca845c@sentry.tellform.com/2').install();
|
||||
</script>
|
||||
|
||||
<!-- [if lt IE 9]>
|
||||
<section class="browsehappy jumbotron hide">
|
||||
<h1>Hello there!</h1>
|
||||
<p>You are using an old browser which we unfortunately do not support.</p>
|
||||
<p>Please <a href="http://browsehappy.com/">click here</a> to update your browser before using the website.</p>
|
||||
<p><a href="http://browsehappy.com" class="btn btn-primary btn-lg" role="button">Yes, upgrade my browser!</a></p>
|
||||
</section>
|
||||
<![endif] -->
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', '{{google_analytics_id}}', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -36,8 +36,12 @@
|
|||
"js-yaml": "^3.6.1",
|
||||
"angular-ui-select": "https://github.com/whitef0x0/ui-select.git#compiled",
|
||||
"angular-translate": "~2.11.0",
|
||||
"ng-device-detector": "~3.0.1",
|
||||
"ng-translate": "*"
|
||||
"ng-device-detector": "^3.0.1",
|
||||
"ng-translate": "*",
|
||||
"deep-diff": "^0.3.4",
|
||||
"mathjs": "^3.4.1",
|
||||
"jsep": "^0.3.1",
|
||||
"ngclipboard": "^1.1.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular-bootstrap": "^0.14.0",
|
||||
|
|
|
@ -31,7 +31,7 @@ if( fs.existsSync('./config/env/api_keys.js') ){
|
|||
exports,
|
||||
require('./env/api_keys')
|
||||
);
|
||||
}else {
|
||||
} else {
|
||||
module.exports = exports;
|
||||
}
|
||||
|
||||
|
|
2
config/env/all.js
vendored
2
config/env/all.js
vendored
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
|
||||
reCAPTCHA_Key: process.env.reCAPTCHA_KEY || '',
|
||||
|
||||
signupDisabled: !!process.env.SIGNUP_DISABLED,
|
||||
signupDisabled: (process.env.SIGNUP_DISABLED === "TRUE"),
|
||||
baseUrl: '',
|
||||
tempUserCollection: 'temporary_users',
|
||||
|
||||
|
|
2
config/env/production.js
vendored
2
config/env/production.js
vendored
|
@ -3,7 +3,7 @@
|
|||
module.exports = {
|
||||
baseUrl: process.env.BASE_URL || 'tellform.com',
|
||||
db: {
|
||||
uri: process.env.MONGODB_URI
|
||||
uri: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean',
|
||||
},
|
||||
port: process.env.PORT || 5000,
|
||||
log: {
|
||||
|
|
|
@ -72,7 +72,6 @@ module.exports = function(db) {
|
|||
var subdomains = req.subdomains;
|
||||
var host = req.hostname;
|
||||
|
||||
|
||||
if(subdomains.slice(0, 4).join('.')+'' === '1.0.0.127'){
|
||||
subdomains = subdomains.slice(4);
|
||||
}
|
||||
|
@ -81,18 +80,38 @@ module.exports = function(db) {
|
|||
if (!subdomains.length) return next();
|
||||
|
||||
var urlPath = url.parse(req.url).path.split('/');
|
||||
if(urlPath.indexOf('static')){
|
||||
if(urlPath.indexOf('static') > -1){
|
||||
//console.log("STATIC FILE\n\n\n\n");
|
||||
urlPath.splice(1,1);
|
||||
req.root = 'https://' + config.baseUrl + urlPath.join('/');
|
||||
console.log(req.root);
|
||||
return next();
|
||||
}
|
||||
|
||||
if(subdomains.indexOf('stage') || subdomains.indexOf('admin')){
|
||||
if(urlPath.indexOf('users') > -1 && urlPath.indexOf('me') > -1){
|
||||
return next();
|
||||
}
|
||||
|
||||
if(subdomains.indexOf('stage') > -1 || subdomains.indexOf('admin') > -1){
|
||||
return next();
|
||||
}
|
||||
|
||||
console.log(req.subdomains.reverse()[0]);
|
||||
//console.log("is api subdomain: "+ (subdomains.indexOf("api") > -1));
|
||||
//console.log(req.url);
|
||||
if(subdomains.indexOf('api') > -1){
|
||||
// rebuild url
|
||||
path += 'api' + req.url;
|
||||
console.log(req.url);
|
||||
// TODO: check path and query strings are preserved
|
||||
// reassign url
|
||||
req.url = path;
|
||||
console.log(req.url);
|
||||
return next();
|
||||
}
|
||||
|
||||
User.findOne({username: req.subdomains.reverse()[0]}).exec(function (err, user) {
|
||||
|
||||
if (err) {
|
||||
console.log(err);
|
||||
req.subdomains = null;
|
||||
|
@ -113,10 +132,13 @@ module.exports = function(db) {
|
|||
|
||||
// TODO: check path and query strings are preserved
|
||||
// reassign url
|
||||
console.log("path: "+path);
|
||||
req.url = path;
|
||||
|
||||
req.userId = user._id;
|
||||
|
||||
console.log('\n\n\ngot subdomain: '+ req.subdomains.reverse()[0]);
|
||||
|
||||
// Q.E.D.
|
||||
next();
|
||||
});
|
||||
|
|
54
config/passport_helpers.js
Normal file
54
config/passport_helpers.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
"use strict";
|
||||
|
||||
var config = require("./config");
|
||||
var passport = require("passport");
|
||||
|
||||
var User = require('mongoose').model('User');
|
||||
|
||||
module.exports.isAuthenticatedOrApiKey = function isAuthenticated(req, res, next) {
|
||||
debugger;
|
||||
if (req.isAuthenticated()) {
|
||||
return next();
|
||||
} else {
|
||||
// Try authenticate with API KEY
|
||||
if (req.headers.apikey || req.query.apikey || req.body.apikey) {
|
||||
passport.authenticate("localapikey", function (err, user, info) {
|
||||
if (err)
|
||||
return res.sendStatus(500);
|
||||
|
||||
if (!user)
|
||||
return res.status(401).send(info.message || "");
|
||||
|
||||
req.login(user, function(err) {
|
||||
if (err) return res.sendStatus(500);
|
||||
|
||||
req.user = user;
|
||||
return next();
|
||||
});
|
||||
|
||||
})(req, res, next);
|
||||
} else {
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports.hasRole = function hasRole(roleRequired) {
|
||||
if (!roleRequired)
|
||||
throw new Error("Required role needs to be set");
|
||||
|
||||
return function(req, res, next) {
|
||||
return module.exports.isAuthenticated(req, res, function() {
|
||||
if (req.user && req.user.roles && req.user.roles.indexOf(roleRequired) !== -1)
|
||||
next();
|
||||
else
|
||||
res.sendStatus(403);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.hasAdminRole = function hasAdminRole() {
|
||||
return module.exports.hasRole("admin");
|
||||
};
|
||||
|
25
config/strategies/apikey.js
Normal file
25
config/strategies/apikey.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
"use strict";
|
||||
|
||||
var passport = require("passport");
|
||||
var LocalAPIKeyStrategy = require("passport-localapikey-update").Strategy;
|
||||
var User = require('mongoose').model('User');
|
||||
|
||||
module.exports = function() {
|
||||
passport.use(new LocalAPIKeyStrategy({
|
||||
passReqToCallback : true
|
||||
}, function(req, apiKey, done) {
|
||||
return User.findOne({
|
||||
"apiKey": apiKey
|
||||
}, function(err, user) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
if (!user)
|
||||
return done(null, false, {
|
||||
message: "Unknown API Key"
|
||||
});
|
||||
|
||||
return done(null, user);
|
||||
});
|
||||
}));
|
||||
};
|
40
dns_masq_setup_osx.md
Normal file
40
dns_masq_setup_osx.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
# wildcard DNS in localhost development
|
||||
- install [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html)
|
||||
```
|
||||
$ brew install dnsmasq
|
||||
...
|
||||
$ cp /usr/local/opt/dnsmasq/dnsmasq.conf.example /usr/local/etc/dnsmasq.conf
|
||||
```
|
||||
- edit `/usr/local/etc/dnsmasq.conf`
|
||||
```
|
||||
address=/local/127.0.0.1
|
||||
```
|
||||
- start **dnsmasq**
|
||||
```
|
||||
$ sudo brew services start dnsmasq
|
||||
```
|
||||
- any time we change `dnsmasq.conf` we have to re-start **dnsmasq**:
|
||||
```
|
||||
$ sudo launchctl stop homebrew.mxcl.dnsmasq
|
||||
$ sudo launchctl start homebrew.mxcl.dnsmasq
|
||||
```
|
||||
- For OS X to _resolve_ requests from `*.local` to **localhost** we need to add a _resolver_:
|
||||
```
|
||||
$ sudo mkdir /etc/resolver
|
||||
$ sudo touch /etc/resolver/local
|
||||
```
|
||||
- edit `/etc/resolver/local`
|
||||
```
|
||||
nameserver 127.0.0.1
|
||||
```
|
||||
- re-start the computer to enable the _resolver_
|
||||
|
||||
===
|
||||
**REFERENCES**
|
||||
|
||||
- [Using Dnsmasq for local development on OS X - Passing Curiosity](https://passingcuriosity.com/2013/dnsmasq-dev-osx/)
|
||||
- [Using Dnsmasq Configure Wildcard DNS Record on Mac | Ri Xu Online](https://xuri.me/2014/12/13/using-dnsmasq-configure-wildcard-dns-record-on-mac.html)
|
||||
- [unix - In my /etc/hosts/ file on Linux/OSX, how do I do a wildcard subdomain? - Server Fault](http://serverfault.com/questions/118378/in-my-etc-hosts-file-on-linux-osx-how-do-i-do-a-wildcard-subdomain)
|
||||
- [hostname - Wildcard in /etc/hosts file - Unix & Linux Stack Exchange](http://unix.stackexchange.com/questions/3352/wildcard-in-etc-hosts-file)
|
||||
- [Mac OS Lion - Wildcard subdomain virtual host - Stack Overflow](http://stackoverflow.com/questions/9562059/mac-os-lion-wildcard-subdomain-virtual-host)
|
||||
- [How to put wildcard entry into /etc/hosts? - Stack Overflow](http://stackoverflow.com/questions/20446930/how-to-put-wildcard-entry-into-etc-hosts)
|
|
@ -1,140 +0,0 @@
|
|||
<?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>
|
File diff suppressed because one or more lines are too long
|
@ -1,108 +0,0 @@
|
|||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.oscarehr.org/">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<ws:addDemographic>
|
||||
<!--Optional:-->
|
||||
<arg0>
|
||||
<!-- <activeCount>1</activeCount>-->
|
||||
<!--Optional:-->
|
||||
<address>2286 Ottawa</address>
|
||||
<!--Optional:-->
|
||||
<!-- <alias>?</alias>-->
|
||||
<!--Optional:-->
|
||||
<!-- <anonymous>?</anonymous>-->
|
||||
<!--Optional:-->
|
||||
<!-- <chartNo>?</chartNo>-->
|
||||
<!--Optional:-->
|
||||
<!-- <children>?</children>-->
|
||||
<!--Optional:-->
|
||||
<!-- <citizenship>?</citizenship>-->
|
||||
<!--Optional:-->
|
||||
<city>West Vancouver</city>
|
||||
<!--Optional:-->
|
||||
<!-- <dateJoined></dateJoined>-->
|
||||
<!--Optional:-->
|
||||
<dateOfBirth>06</dateOfBirth>
|
||||
<!--Optional:-->
|
||||
<!-- <demographicNo>?</demographicNo>-->
|
||||
<!--Optional:-->
|
||||
<displayName>TestUserAddDemographic</displayName>
|
||||
<!--Optional:-->
|
||||
<!-- <effDate></effDate>-->
|
||||
<!--Optional:-->
|
||||
<email>polydaic@gmail.com</email>
|
||||
<!--Optional:-->
|
||||
<!-- <endDate></endDate>-->
|
||||
<!--Optional:-->
|
||||
<!-- <familyDoctor> </familyDoctor>-->
|
||||
<!--Optional:-->
|
||||
<firstName>TestUser</firstName>
|
||||
<!--Optional:-->
|
||||
<!-- <hcRenewDate></hcRenewDate>-->
|
||||
<!--Optional:-->
|
||||
<!-- <hcType></hcType>-->
|
||||
<!--Optional:-->
|
||||
<!-- <headRecord></headRecord>-->
|
||||
<!--Optional:-->
|
||||
<!-- <hin></hin>-->
|
||||
<!-- <hsAlertCount>0</hsAlertCount>-->
|
||||
<!--Optional:-->
|
||||
<lastName>AddDemographic</lastName>
|
||||
<!--Optional:-->
|
||||
<lastUpdateDate>2015-10-01T18:39:46.817Z</lastUpdateDate>
|
||||
<!--Optional:-->
|
||||
<!-- <lastUpdateUser> </lastUpdateUser>-->
|
||||
<!--Optional:-->
|
||||
<!-- <links> </links>-->
|
||||
<!--Optional:-->
|
||||
<monthOfBirth>06</monthOfBirth>
|
||||
<!--Optional:-->
|
||||
<!-- <myOscarUserName> </myOscarUserName>-->
|
||||
<!--Optional:-->
|
||||
<!-- <officialLanguage> </officialLanguage>-->
|
||||
<!--Optional:-->
|
||||
<!-- <patientStatus> </patientStatus>-->
|
||||
<!--Optional:-->
|
||||
<!-- <patientStatusDate></patientStatusDate>-->
|
||||
<!--Optional:-->
|
||||
<!-- <pcnIndicator> </pcnIndicator>-->
|
||||
<!--Optional:-->
|
||||
<phone>6048786969</phone>
|
||||
<!--Optional:-->
|
||||
<!-- <phone2> </phone2>-->
|
||||
<!--Optional:-->
|
||||
<postal>V8V5S8</postal>
|
||||
<!--Optional:-->
|
||||
<!-- <previousAddress> </previousAddress>-->
|
||||
<!--Optional:-->
|
||||
<!-- <providerNo> </providerNo>-->
|
||||
<!--Optional:-->
|
||||
<province>BC</province>
|
||||
<!--Optional:-->
|
||||
<!-- <rosterDate></rosterDate>-->
|
||||
<!--Optional:-->
|
||||
<!-- <rosterStatus> </rosterStatus>-->
|
||||
<!--Optional:-->
|
||||
<!-- <rosterTerminationDate></rosterTerminationDate>-->
|
||||
<!--Optional:-->
|
||||
<!-- <rosterTerminationReason> </rosterTerminationReason>-->
|
||||
<!--Optional:-->
|
||||
<sex>M</sex>
|
||||
<!--Optional:-->
|
||||
<!-- <sexDesc> </sexDesc>-->
|
||||
<!--Optional:-->
|
||||
<!-- <sin> </sin>-->
|
||||
<!--Optional:-->
|
||||
<!-- <sourceOfIncome> </sourceOfIncome>-->
|
||||
<!--Optional:-->
|
||||
<!-- <spokenLanguage> </spokenLanguage>-->
|
||||
<!--Optional:-->
|
||||
<title>Mr.</title>
|
||||
<!--Optional:-->
|
||||
<!-- <ver> </ver>-->
|
||||
<!--Optional:-->
|
||||
<yearOfBirth>1994</yearOfBirth>
|
||||
</arg0>
|
||||
</ws:addDemographic>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
12
package.json
12
package.json
|
@ -24,6 +24,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"async": "^1.4.2",
|
||||
"async-boolean-expression-evaluator": "^1.1.1",
|
||||
"aws-sdk": "^2.3.9",
|
||||
"bcrypt": "^0.8.7",
|
||||
"body-parser": "~1.14.1",
|
||||
|
@ -35,6 +36,7 @@
|
|||
"connect-mongo": "~0.8.2",
|
||||
"consolidate": "~0.13.1",
|
||||
"cookie-parser": "~1.4.0",
|
||||
"deep-diff": "^0.3.4",
|
||||
"dotenv": "^2.0.0",
|
||||
"email-verification": "~0.4.1",
|
||||
"envfile": "^2.0.1",
|
||||
|
@ -64,6 +66,7 @@
|
|||
"lodash": "^2.4.1",
|
||||
"main-bower-files": "~2.9.0",
|
||||
"math": "0.0.3",
|
||||
"mathjs": "^3.4.1",
|
||||
"method-override": "~2.3.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mongoose": "~4.4.19",
|
||||
|
@ -81,6 +84,7 @@
|
|||
"passport-google-oauth": "~0.2.0",
|
||||
"passport-linkedin": "~1.0.0",
|
||||
"passport-local": "~1.0.0",
|
||||
"passport-localapikey-update": "^0.5.0",
|
||||
"passport-twitter": "~1.0.2",
|
||||
"path-exists": "^2.1.0",
|
||||
"pdffiller": "~0.1.1",
|
||||
|
@ -90,10 +94,14 @@
|
|||
"socket.io": "^1.4.6",
|
||||
"socket.io-redis": "^1.0.0",
|
||||
"swig": "~1.4.1",
|
||||
"uuid-token-generator": "^0.5.0",
|
||||
"wildcard-subdomains": "github:whitef0x0/wildcard-subdomains"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chromedriver": "^2.25.1",
|
||||
"coveralls": "^2.11.4",
|
||||
"cross-spawn": "^5.0.0",
|
||||
"del": "^2.2.2",
|
||||
"glob": "^7.0.3",
|
||||
"grunt-execute": "^0.2.2",
|
||||
"grunt-mocha-istanbul": "^3.0.1",
|
||||
|
@ -110,10 +118,12 @@
|
|||
"karma-ng-html2js-preprocessor": "^0.2.0",
|
||||
"karma-phantomjs-launcher": "~0.2.1",
|
||||
"mailosaur": "^1.0.1",
|
||||
"mocha": ">=1.20.0",
|
||||
"mocha": "^3.1.2",
|
||||
"mocha-lcov-reporter": "^1.0.0",
|
||||
"nightwatch": "^0.9.8",
|
||||
"node-mandrill": "^1.0.1",
|
||||
"phantomjs": "^1.9.18",
|
||||
"selenium-server": "^3.0.1",
|
||||
"should": "~7.1.1",
|
||||
"supertest": "~1.2.0",
|
||||
"supertest-session": "~2.0.1"
|
||||
|
|
272
public/dist/application.js
vendored
272
public/dist/application.js
vendored
File diff suppressed because one or more lines are too long
4
public/dist/application.min.css
vendored
4
public/dist/application.min.css
vendored
File diff suppressed because one or more lines are too long
10
public/dist/application.min.js
vendored
10
public/dist/application.min.js
vendored
File diff suppressed because one or more lines are too long
371
public/dist/form-application.js
vendored
371
public/dist/form-application.js
vendored
File diff suppressed because one or more lines are too long
7
public/dist/form-application.min.js
vendored
7
public/dist/form-application.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -2,107 +2,108 @@
|
|||
|
||||
// coffeescript's for in loop
|
||||
var __indexOf = [].indexOf || function(item) {
|
||||
for (var i = 0, l = this.length; i < l; i++) {
|
||||
if (i in this && this[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
for (var i = 0, l = this.length; i < l; i++) {
|
||||
if (i in this && this[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
angular.module('view-form').directive('fieldDirective', ['$http', '$compile', '$rootScope', '$templateCache', 'supportedFields',
|
||||
function($http, $compile, $rootScope, $templateCache, supportedFields) {
|
||||
function($http, $compile, $rootScope, $templateCache, supportedFields) {
|
||||
|
||||
var getTemplateUrl = function(fieldType) {
|
||||
var type = fieldType;
|
||||
var getTemplateUrl = function(fieldType) {
|
||||
var type = fieldType;
|
||||
|
||||
var supported_fields = [
|
||||
'textfield',
|
||||
'textarea',
|
||||
'date',
|
||||
'dropdown',
|
||||
'hidden',
|
||||
'password',
|
||||
'radio',
|
||||
'legal',
|
||||
'statement',
|
||||
'rating',
|
||||
'yes_no',
|
||||
'number',
|
||||
'natural'
|
||||
];
|
||||
var supported_fields = [
|
||||
'textfield',
|
||||
'textarea',
|
||||
'date',
|
||||
'dropdown',
|
||||
'hidden',
|
||||
'password',
|
||||
'radio',
|
||||
'legal',
|
||||
'statement',
|
||||
'rating',
|
||||
'yes_no',
|
||||
'number',
|
||||
'natural'
|
||||
];
|
||||
|
||||
var templateUrl = 'modules/forms/base/views/directiveViews/field/';
|
||||
var templateUrl = 'modules/forms/base/views/directiveViews/field/';
|
||||
|
||||
if (__indexOf.call(supportedFields, type) >= 0) {
|
||||
templateUrl = templateUrl+type+'.html';
|
||||
}
|
||||
return $templateCache.get(templateUrl);
|
||||
};
|
||||
|
||||
return {
|
||||
template: '<div>{{field.title}}</div>',
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
field: '=',
|
||||
required: '&',
|
||||
design: '=',
|
||||
index: '=',
|
||||
forms: '='
|
||||
},
|
||||
link: function(scope, element) {
|
||||
|
||||
$rootScope.chooseDefaultOption = scope.chooseDefaultOption = function(type) {
|
||||
if(type === 'yes_no'){
|
||||
scope.field.fieldValue = 'true';
|
||||
}else if(type === 'rating'){
|
||||
scope.field.fieldValue = 0;
|
||||
}else if(scope.field.fieldType === 'radio'){
|
||||
console.log(scope.field);
|
||||
scope.field.fieldValue = scope.field.fieldOptions[0].option_value;
|
||||
console.log(scope.field.fieldValue);
|
||||
}else if(type === 'legal'){
|
||||
scope.field.fieldValue = 'true';
|
||||
$rootScope.nextField();
|
||||
}
|
||||
};
|
||||
|
||||
scope.setActiveField = $rootScope.setActiveField;
|
||||
|
||||
//Set format only if field is a date
|
||||
if(scope.field.fieldType === 'date'){
|
||||
scope.dateOptions = {
|
||||
changeYear: true,
|
||||
changeMonth: true,
|
||||
altFormat: 'mm/dd/yyyy',
|
||||
yearRange: '1900:-0',
|
||||
defaultDate: 0
|
||||
};
|
||||
}
|
||||
|
||||
var fieldType = scope.field.fieldType;
|
||||
|
||||
if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){
|
||||
switch(scope.field.fieldType){
|
||||
case 'textfield':
|
||||
scope.field.input_type = 'text';
|
||||
break;
|
||||
case 'email':
|
||||
scope.field.input_type = 'email';
|
||||
scope.field.placeholder = 'joesmith@example.com';
|
||||
break;
|
||||
case 'number':
|
||||
scope.field.input_type = 'text';
|
||||
scope.field.validateRegex = /^-?\d+$/;
|
||||
break;
|
||||
default:
|
||||
scope.field.input_type = 'url';
|
||||
scope.field.placeholder = 'http://example.com';
|
||||
break;
|
||||
}
|
||||
fieldType = 'textfield';
|
||||
if (__indexOf.call(supportedFields, type) >= 0) {
|
||||
templateUrl = templateUrl+type+'.html';
|
||||
}
|
||||
var template = getTemplateUrl(fieldType);
|
||||
element.html(template).show();
|
||||
var output = $compile(element.contents())(scope);
|
||||
}
|
||||
};
|
||||
}]);
|
||||
return $templateCache.get(templateUrl);
|
||||
};
|
||||
|
||||
return {
|
||||
template: '<div>{{field.title}}</div>',
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
field: '=',
|
||||
required: '&',
|
||||
design: '=',
|
||||
index: '=',
|
||||
forms: '='
|
||||
},
|
||||
link: function(scope, element) {
|
||||
|
||||
$rootScope.chooseDefaultOption = scope.chooseDefaultOption = function(type) {
|
||||
if(type === 'yes_no'){
|
||||
scope.field.fieldValue = 'true';
|
||||
}else if(type === 'rating'){
|
||||
scope.field.fieldValue = 0;
|
||||
}else if(scope.field.fieldType === 'radio'){
|
||||
console.log(scope.field);
|
||||
scope.field.fieldValue = scope.field.fieldOptions[0].option_value;
|
||||
console.log(scope.field.fieldValue);
|
||||
}else if(type === 'legal'){
|
||||
scope.field.fieldValue = 'true';
|
||||
$rootScope.nextField();
|
||||
}
|
||||
};
|
||||
|
||||
scope.setActiveField = $rootScope.setActiveField;
|
||||
|
||||
//Set format only if field is a date
|
||||
if(scope.field.fieldType === 'date'){
|
||||
scope.dateOptions = {
|
||||
changeYear: true,
|
||||
changeMonth: true,
|
||||
altFormat: 'mm/dd/yyyy',
|
||||
yearRange: '1900:-0',
|
||||
defaultDate: 0
|
||||
};
|
||||
}
|
||||
|
||||
var fieldType = scope.field.fieldType;
|
||||
|
||||
if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){
|
||||
switch(scope.field.fieldType){
|
||||
case 'textfield':
|
||||
scope.input_type = 'text';
|
||||
break;
|
||||
case 'email':
|
||||
scope.input_type = 'email';
|
||||
scope.placeholder = 'joesmith@example.com';
|
||||
break;
|
||||
case 'number':
|
||||
scope.input_type = 'text';
|
||||
scope.validateRegex = /^-?\d+$/;
|
||||
break;
|
||||
default:
|
||||
scope.input_type = 'url';
|
||||
scope.placeholder = 'http://example.com';
|
||||
break;
|
||||
}
|
||||
fieldType = 'textfield';
|
||||
}
|
||||
|
||||
var template = getTemplateUrl(fieldType);
|
||||
element.html(template).show();
|
||||
var output = $compile(element.contents())(scope);
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
|
|
@ -1,5 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
//FIXME: Should find an appropriate place for this
|
||||
//Setting up jsep
|
||||
jsep.addBinaryOp("contains", 10);
|
||||
jsep.addBinaryOp("!contains", 10);
|
||||
jsep.addBinaryOp("begins", 10);
|
||||
jsep.addBinaryOp("!begins", 10);
|
||||
jsep.addBinaryOp("ends", 10);
|
||||
jsep.addBinaryOp("!ends", 10);
|
||||
|
||||
/**
|
||||
* Calculate a 32 bit FNV-1a hash
|
||||
* Found here: https://gist.github.com/vaiorabbit/5657561
|
||||
* Ref.: http://isthe.com/chongo/tech/comp/fnv/
|
||||
*
|
||||
* @param {string} str the input value
|
||||
* @param {boolean} [asString=false] set to true to return the hash value as
|
||||
* 8-digit hex string instead of an integer
|
||||
* @param {integer} [seed] optionally pass the hash of the previous chunk
|
||||
* @returns {integer | string}
|
||||
*/
|
||||
function hashFnv32a(str, asString, seed) {
|
||||
/*jshint bitwise:false */
|
||||
var i, l,
|
||||
hval = (seed === undefined) ? 0x811c9dc5 : seed;
|
||||
|
||||
for (i = 0, l = str.length; i < l; i++) {
|
||||
hval ^= str.charCodeAt(i);
|
||||
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
|
||||
}
|
||||
if( asString ){
|
||||
// Convert to 8 digit hex string
|
||||
return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
|
||||
}
|
||||
return hval >>> 0;
|
||||
}
|
||||
;
|
||||
|
||||
angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCounter', '$filter', '$rootScope', 'SendVisitorData',
|
||||
function ($http, TimeCounter, $filter, $rootScope, SendVisitorData) {
|
||||
|
@ -92,6 +128,67 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
|
|||
/*
|
||||
** Field Controls
|
||||
*/
|
||||
var evaluateLogicJump = function(field){
|
||||
console.log('evaluateLogicJump');
|
||||
console.log(field.fieldValue);
|
||||
var logicJump = field.logicJump;
|
||||
|
||||
if (logicJump.expressionString && logicJump.valueB && field.fieldValue) {
|
||||
var parse_tree = jsep(logicJump.expressionString);
|
||||
var left, right;
|
||||
|
||||
console.log(parse_tree);
|
||||
|
||||
if(parse_tree.left.name === 'field'){
|
||||
left = field.fieldValue;
|
||||
right = logicJump.valueB
|
||||
} else {
|
||||
left = logicJump.valueB;
|
||||
right = field.fieldValue;
|
||||
}
|
||||
|
||||
if(field.fieldType === 'number' || field.fieldType === 'scale' || field.fieldType === 'rating'){
|
||||
switch(parse_tree.operator) {
|
||||
case '==':
|
||||
return (parseInt(left) === parseInt(right));
|
||||
case '!==':
|
||||
return (parseInt(left) !== parseInt(right));
|
||||
case '>':
|
||||
return (parseInt(left) > parseInt(right));
|
||||
case '>=':
|
||||
return (parseInt(left) > parseInt(right));
|
||||
case '<':
|
||||
return (parseInt(left) < parseInt(right));
|
||||
case '<=':
|
||||
return (parseInt(left) <= parseInt(right));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
switch(parse_tree.operator) {
|
||||
case '==':
|
||||
return (left === right);
|
||||
case '!==':
|
||||
return (left !== right);
|
||||
case 'contains':
|
||||
return (left.indexOf(right) > -1);
|
||||
case '!contains':
|
||||
return !(left.indexOf(right) > -1);
|
||||
case 'begins':
|
||||
return left.startsWith(right);
|
||||
case '!begins':
|
||||
return !left.startsWith(right);
|
||||
case 'ends':
|
||||
return left.endsWith(right);
|
||||
case '!ends':
|
||||
return left.endsWith(right);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var getActiveField = function(){
|
||||
if($scope.selected === null){
|
||||
console.error('current active field is null');
|
||||
|
@ -117,6 +214,15 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
|
|||
|
||||
$scope.selected._id = field_id;
|
||||
$scope.selected.index = field_index;
|
||||
if(!field_index){
|
||||
for(var i=0; i<$scope.myform.visible_form_fields.length; i++){
|
||||
var currField = $scope.myform.visible_form_fields[i];
|
||||
if(field_id == currField._id){
|
||||
$scope.selected.index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nb_valid = $filter('formValidity')($scope.myform);
|
||||
$scope.translateAdvancementData = {
|
||||
|
@ -159,20 +265,26 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
|
|||
};
|
||||
|
||||
$rootScope.nextField = $scope.nextField = function(){
|
||||
//console.log('nextfield');
|
||||
//console.log($scope.selected.index);
|
||||
//console.log($scope.myform.visible_form_fields.length-1);
|
||||
var selected_index, selected_id;
|
||||
if($scope.selected.index < $scope.myform.visible_form_fields.length-1){
|
||||
selected_index = $scope.selected.index+1;
|
||||
selected_id = $scope.myform.visible_form_fields[selected_index]._id;
|
||||
$rootScope.setActiveField(selected_id, selected_index, true);
|
||||
} else if($scope.selected.index === $scope.myform.visible_form_fields.length-1) {
|
||||
//console.log('Second last element');
|
||||
selected_index = $scope.selected.index+1;
|
||||
selected_id = 'submit_field';
|
||||
$rootScope.setActiveField(selected_id, selected_index, true);
|
||||
var currField = $scope.myform.visible_form_fields[$scope.selected.index];
|
||||
|
||||
if($scope.selected && $scope.selected.index > -1){
|
||||
//Jump to logicJump's destination if it is true
|
||||
if(currField.logicJump && evaluateLogicJump(currField)){
|
||||
$rootScope.setActiveField(currField.logicJump.jumpTo, null, true);
|
||||
} else {
|
||||
var selected_index, selected_id;
|
||||
if($scope.selected.index < $scope.myform.visible_form_fields.length-1){
|
||||
selected_index = $scope.selected.index+1;
|
||||
selected_id = $scope.myform.visible_form_fields[selected_index]._id;
|
||||
$rootScope.setActiveField(selected_id, selected_index, true);
|
||||
} else if($scope.selected.index === $scope.myform.visible_form_fields.length-1) {
|
||||
selected_index = $scope.selected.index+1;
|
||||
selected_id = 'submit_field';
|
||||
$rootScope.setActiveField(selected_id, selected_index, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$rootScope.prevField = $scope.prevField = function(){
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
<div class="col-xs-12 field-input">
|
||||
<input ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}"
|
||||
name="{{field.fieldType}}{{index}}"
|
||||
type="{{field.input_type}}"
|
||||
ng-pattern="field.validateRegex"
|
||||
placeholder="{{field.placeholder}}"
|
||||
type="{{input_type}}"
|
||||
ng-pattern="validateRegex"
|
||||
placeholder="{{placeholder}}"
|
||||
ng-class="{ 'no-border': !!field.fieldValue }"
|
||||
class="focusOn text-field-input"
|
||||
ng-model="field.fieldValue"
|
||||
|
@ -34,7 +34,7 @@
|
|||
on-tab-and-shift-key="prevField()"
|
||||
ng-required="field.required"
|
||||
ng-disabled="field.disabled"
|
||||
aria-describedby="inputError2Status">
|
||||
aria-describedby="inputError2Status"/ >
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<div ng-show="forms.myForm.{{field.fieldType}}{{index}}.$invalid && !!forms.myForm.{{field.fieldType}}{{index}}.$viewValue " class="alert alert-danger" role="alert">
|
||||
|
@ -51,7 +51,7 @@
|
|||
style="padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)">
|
||||
<button ng-disabled="!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid"
|
||||
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
|
||||
ng-click="$root.nextField()"
|
||||
ng-click="nextField()"
|
||||
class="btn col-sm-5 col-xs-5">
|
||||
|
||||
{{ 'OK' | translate }} <i class="fa fa-check"></i>
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
ApplicationConfiguration.registerModule('view-form', [
|
||||
'ngFileUpload', 'ui.router.tabs', 'ui.date', 'ui.sortable',
|
||||
'angular-input-stars', 'pascalprecht.translate'
|
||||
]);//, 'colorpicker.module' @TODO reactivate this module
|
||||
]);
|
||||
|
|
|
@ -6,11 +6,12 @@ angular.module('core').controller('HeaderController', ['$rootScope', '$scope', '
|
|||
$rootScope.signupDisabled = $window.signupDisabled;
|
||||
|
||||
$scope.user = $rootScope.user = Auth.ensureHasCurrentUser(User);
|
||||
|
||||
console.log(Auth.ensureHasCurrentUser(User));
|
||||
$scope.authentication = $rootScope.authentication = Auth;
|
||||
|
||||
$rootScope.languages = $scope.languages = ['en', 'fr', 'es', 'it', 'de'];
|
||||
|
||||
console.log($locale.id);
|
||||
//Set global app language
|
||||
if($scope.authentication.isAuthenticated()){
|
||||
$rootScope.language = $scope.user.language;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
|
@ -10,12 +12,34 @@ body {
|
|||
float: none;
|
||||
}
|
||||
|
||||
/* Custom CSS for Buttons */
|
||||
.btn-rounded {
|
||||
border-radius: 100px;
|
||||
font-size: 14px;
|
||||
padding: 10px 28px;
|
||||
margin: 0 2px;
|
||||
margin-top: 1em;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none!important;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #DDDDDD;
|
||||
color: #4c4c4c;
|
||||
border: 2px #4c4c4c solid;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: #cacaca;
|
||||
border-color: #cacaca;
|
||||
}
|
||||
|
||||
/*Navbar Custom CSS*/
|
||||
.navbar {
|
||||
min-height: 60px;
|
||||
padding: 10px 0 10px 0;
|
||||
}
|
||||
.navbar-inverse {
|
||||
background-color:#588EB4;
|
||||
background-color:#3FA2F7;
|
||||
border: 0;
|
||||
color: white!important;
|
||||
}
|
||||
|
@ -33,6 +57,7 @@ body {
|
|||
min-height: 60px;
|
||||
}
|
||||
.navbar-nav > li > a {
|
||||
padding-top: 20px;
|
||||
color: white;
|
||||
}
|
||||
.navbar-nav > li:hover, .navbar-nav > li.active {
|
||||
|
|
|
@ -5,25 +5,11 @@
|
|||
<span class="sr-only">Toggle navigation</span>
|
||||
<span>{{ 'MENU_BTN' | translate }}</span>
|
||||
</button>
|
||||
<a href="https://www.tellform.com/#!/" class="navbar-brand">
|
||||
<a href="/#!/" class="navbar-brand">
|
||||
<img src="/static/modules/core/img/logo_white.svg" height="100%">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="collapse navbar-collapse" collapse="!isCollapsed" role="navigation">
|
||||
<ul class="nav navbar-nav" data-ng-if="authentication.isAuthenticated()">
|
||||
<li data-ng-repeat="item in menu.items | orderBy: 'position'" data-ng-if="item.shouldRender(authentication.isAuthenticated());" ng-switch="item.menuItemType" ui-route="{{item.uiRoute}}" class="{{item.menuItemClass}}" ng-class="{active: ($uiRoute)}" dropdown="item.menuItemType === 'dropdown'">
|
||||
<a ng-switch-when="dropdown" class="dropdown-toggle" dropdown-toggle>
|
||||
<span data-ng-bind="item.title"></span>
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul ng-switch-when="dropdown" class="dropdown-menu">
|
||||
<li data-ng-repeat="subitem in item.items | orderBy: 'position'" data-ng-if="subitem.shouldRender(authentication.isAuthenticated());" ui-route="{{subitem.uiRoute}}" ng-class="{active: $uiRoute}">
|
||||
<a href="/#!/{{subitem.link}}" data-ng-bind="subitem.title"></a>
|
||||
</li>
|
||||
</ul>
|
||||
<a ng-switch-default href="/#!/{{item.link}}" data-ng-bind="item.title"></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right" data-ng-hide="authentication.isAuthenticated()">
|
||||
<li ng-hide="$root.signupDisabled" ui-route="/signup" ng-class="{active: $uiRoute}">
|
||||
<a href="/#!/signup">{{ 'SIGNUP_TAB' | translate }}</a>
|
||||
|
|
|
@ -39,6 +39,10 @@ angular.module('forms').config(['$translateProvider', function ($translateProvid
|
|||
PREVIEW: 'Preview',
|
||||
|
||||
//Edit Form View
|
||||
DISABLED: 'Disabled:',
|
||||
YES: 'YES',
|
||||
NO: 'NO',
|
||||
ADD_LOGIC_JUMP: 'Add Logic Jump',
|
||||
ADD_FIELD_LG: 'Click to Add New Field',
|
||||
ADD_FIELD_MD: 'Add New Field',
|
||||
ADD_FIELD_SM: 'Add Field',
|
||||
|
|
|
@ -1,8 +1,21 @@
|
|||
'use strict';
|
||||
|
||||
// Forms controller
|
||||
angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope', '$stateParams', '$state', 'Forms', 'CurrentForm', '$http', '$uibModal', 'myForm', '$filter',
|
||||
function($rootScope, $scope, $stateParams, $state, Forms, CurrentForm, $http, $uibModal, myForm, $filter) {
|
||||
angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope', '$stateParams', '$state', 'Forms', 'CurrentForm', '$http', '$uibModal', 'myForm', '$filter', '$sce',
|
||||
function($rootScope, $scope, $stateParams, $state, Forms, CurrentForm, $http, $uibModal, myForm, $filter, $sce) {
|
||||
|
||||
$scope.trustSrc = function(src) {
|
||||
return $sce.trustAsResourceUrl(src);
|
||||
};
|
||||
|
||||
//Set active tab to Create
|
||||
$scope.activePill = 0;
|
||||
|
||||
$scope.copied = false;
|
||||
$scope.onCopySuccess = function(e) {
|
||||
console.log("COPY SUCCESSFUL!");
|
||||
$scope.copied = true;
|
||||
};
|
||||
|
||||
$scope = $rootScope;
|
||||
$scope.animationsEnabled = true;
|
||||
|
@ -11,24 +24,38 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope
|
|||
|
||||
CurrentForm.setForm($scope.myform);
|
||||
|
||||
$scope.formURL = $scope.myform.admin.username + '.tellform.com';
|
||||
$scope.formURL = "/#!/forms/" + $scope.myform._id;
|
||||
|
||||
$scope.tabData = [
|
||||
{
|
||||
if(window.location.host.split('.') < 3){
|
||||
$scope.actualFormURL = window.location.protocol + '//' + $scope.myform.admin.username + '.' + window.location.host + "/#!/forms/" + $scope.myform._id;
|
||||
} else {
|
||||
$scope.actualFormURL = window.location.protocol + '//' + $scope.myform.admin.username + '.' + window.location.host.split('.').slice(1,3).join('.') + "/#!/forms/" + $scope.myform._id;
|
||||
}
|
||||
|
||||
|
||||
var refreshFrame = $scope.refreshFrame = function(){
|
||||
if(document.getElementById('iframe')) {
|
||||
document.getElementById('iframe').contentWindow.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.tabData = [
|
||||
/*{
|
||||
heading: $filter('translate')('CREATE_TAB'),
|
||||
route: 'viewForm.create'
|
||||
templateName: 'create'
|
||||
},
|
||||
{
|
||||
heading: $filter('translate')('DESIGN_TAB'),
|
||||
route: 'viewForm.design'
|
||||
},
|
||||
templateName: 'design'
|
||||
},*/
|
||||
{
|
||||
heading: $filter('translate')('CONFIGURE_TAB'),
|
||||
route: 'viewForm.configure'
|
||||
templateName: 'configure'
|
||||
},
|
||||
{
|
||||
heading: $filter('translate')('ANALYZE_TAB'),
|
||||
route: 'viewForm.analyze'
|
||||
templateName: 'analyze'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -92,7 +119,8 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope
|
|||
};
|
||||
|
||||
// Update existing Form
|
||||
$scope.update = $rootScope.update = function(updateImmediately, cb){
|
||||
$scope.update = $rootScope.update = function(updateImmediately, diffChanges, cb){
|
||||
refreshFrame();
|
||||
|
||||
var continueUpdate = true;
|
||||
if(!updateImmediately){
|
||||
|
@ -105,7 +133,7 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope
|
|||
|
||||
if(!updateImmediately){ $rootScope.saveInProgress = true; }
|
||||
|
||||
$scope.updatePromise = $http.put('/forms/'+$scope.myform._id, {form: $scope.myform})
|
||||
$scope.updatePromise = $http.put('/forms/'+$scope.myform._id, { changes: diffChanges })
|
||||
.then(function(response){
|
||||
$rootScope.myform = $scope.myform = response.data;
|
||||
// console.log(response.data);
|
||||
|
|
|
@ -32,7 +32,6 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
|
|||
|
||||
$rootScope.finishedRender = false;
|
||||
$scope.$on('editFormFields Started', function(ngRepeatFinishedEvent) {
|
||||
// console.log('hello');
|
||||
$rootScope.finishedRender = false;
|
||||
});
|
||||
$scope.$on('editFormFields Finished', function(ngRepeatFinishedEvent) {
|
||||
|
@ -52,13 +51,11 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
|
|||
return false;
|
||||
};
|
||||
|
||||
var debounceSave = function () {
|
||||
$rootScope.saveInProgress = true;
|
||||
var debounceSave = function (diffChanges) {
|
||||
|
||||
$rootScope[$attrs.autoSaveCallback](true,
|
||||
$rootScope[$attrs.autoSaveCallback](true, diffChanges,
|
||||
function(err){
|
||||
if(!err){
|
||||
//console.log('\n\nForm data persisted -- setting pristine flag');
|
||||
$formCtrl.$setPristine();
|
||||
$formCtrl.$setUntouched();
|
||||
}else{
|
||||
|
@ -68,56 +65,48 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
|
|||
});
|
||||
};
|
||||
|
||||
//Update/Save Form if any Form fields are Dirty and Touched
|
||||
$scope.$watch(function(newValue, oldValue) {
|
||||
//console.log('introParagraphStartPage.$dirty: '+$scope.editForm.introParagraphStartPage.$dirty);
|
||||
//console.log('introParagraphStartPage.$touched: '+$scope.editForm.introParagraphStartPage.$touched);
|
||||
if($rootScope.finishedRender && $scope.anyDirtyAndTouched($scope.editForm) && !$rootScope.saveInProgress){
|
||||
//console.log('Form saving started');
|
||||
debounceSave();
|
||||
//console.log('introParagraphStartPage.$dirty AFTER: '+$scope.editForm.introParagraphStartPage.$dirty);
|
||||
}
|
||||
});
|
||||
|
||||
//Autosave Form when model (specified in $attrs.autoSaveWatch) changes
|
||||
$scope.$watch($attrs.autoSaveWatch, function(newValue, oldValue) {
|
||||
|
||||
if( !newValue || !oldValue ) {
|
||||
$rootScope.finishedRender = true;
|
||||
return;
|
||||
}
|
||||
|
||||
newValue = angular.copy(newValue);
|
||||
oldValue = angular.copy(oldValue);
|
||||
|
||||
newValue.form_fields = _.removeDateFields(newValue.form_fields);
|
||||
oldValue.form_fields = _.removeDateFields(oldValue.form_fields);
|
||||
delete newValue.visible_form_fields;
|
||||
delete oldValue.visible_form_fields;
|
||||
newValue.form_fields = _.removeDateFields(newValue.form_fields);
|
||||
oldValue.form_fields = _.removeDateFields(oldValue.form_fields);
|
||||
|
||||
var changedFields = !_.isEqual(oldValue.form_fields,newValue.form_fields) || !_.isEqual(oldValue.startPage, newValue.startPage);
|
||||
var changedFieldMap = false;
|
||||
var changedFields = !!DeepDiff.diff(oldValue, newValue) && DeepDiff.diff(oldValue, newValue).length > 0;
|
||||
|
||||
if(oldValue.hasOwnProperty('plugins.oscarhost.settings.fieldMap')){
|
||||
changedFieldMap = !!oldValue.plugins.oscarhost.settings.fieldMap && !_.isEqual(oldValue.plugins.oscarhost.settings.fieldMap,newValue.plugins.oscarhost.settings.fieldMap);
|
||||
}
|
||||
//If our form is undefined, don't save form
|
||||
if(!changedFields){
|
||||
$rootScope.finishedRender = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//If our form is undefined, don't save form
|
||||
if( (!newValue && !oldValue) || !oldValue ){
|
||||
return;
|
||||
}
|
||||
if(oldValue.form_fields.length === 0) {
|
||||
$rootScope.finishedRender = true;
|
||||
}
|
||||
|
||||
//console.log('Autosaving');
|
||||
//console.log('\n\n----------');
|
||||
//console.log('!$dirty: '+ !$formCtrl.$dirty );
|
||||
|
||||
// console.log('Autosaving');
|
||||
// console.log('\n\n----------');
|
||||
// console.log('!$dirty: '+ !$formCtrl.$dirty );
|
||||
// console.log('changedFields: '+changedFields);
|
||||
// console.log('changedFieldMap: '+changedFieldMap);
|
||||
// console.log('finishedRender: '+$rootScope.finishedRender);
|
||||
// console.log('!saveInProgress: '+!$rootScope.saveInProgress);
|
||||
//console.log('finishedRender: '+$rootScope.finishedRender);
|
||||
//console.log('!saveInProgress: '+!$rootScope.saveInProgress);
|
||||
// console.log('newValue: '+newValue);
|
||||
// console.log('oldValue: '+oldValue);
|
||||
// console.log(oldValue.form_fields);
|
||||
// console.log(newValue.form_fields);
|
||||
|
||||
if(oldValue.form_fields.length === 0) {
|
||||
$rootScope.finishedRender = true;
|
||||
}
|
||||
|
||||
//Save form ONLY IF rendering is finished, form_fields have been changed AND currently not save in progress
|
||||
if( $rootScope.finishedRender && ((changedFields && !$formCtrl.$dirty) || changedFieldMap) && !$rootScope.saveInProgress) {
|
||||
if( $rootScope.finishedRender && (changedFields) && !$rootScope.saveInProgress) {
|
||||
|
||||
if(savePromise) {
|
||||
$timeout.cancel(savePromise);
|
||||
|
@ -125,7 +114,12 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
|
|||
}
|
||||
|
||||
savePromise = $timeout(function() {
|
||||
debounceSave();
|
||||
$rootScope.saveInProgress = true;
|
||||
|
||||
delete newValue.visible_form_fields;
|
||||
delete newValue.visible_form_fields;
|
||||
var _diff = DeepDiff.diff(oldValue, newValue);
|
||||
debounceSave(_diff);
|
||||
});
|
||||
}
|
||||
//If we are finished rendering then form saving should be finished
|
||||
|
|
|
@ -5,16 +5,12 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
|||
return {
|
||||
templateUrl: 'modules/forms/admin/views/directiveViews/form/edit-form.client.view.html',
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {
|
||||
myform:'='
|
||||
myform:'='
|
||||
},
|
||||
controller: function($scope){
|
||||
|
||||
console.log($scope.myform);
|
||||
var field_ids = _($scope.myform.form_fields).pluck('_id');
|
||||
for(var i=0; i<field_ids.length; i++){
|
||||
$scope.myform.plugins.oscarhost.settings.fieldMap[field_ids[i]] = null;
|
||||
}
|
||||
/*
|
||||
** Initialize scope with variables
|
||||
*/
|
||||
|
@ -63,24 +59,24 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
|||
//Populate local scope with rootScope methods/variables
|
||||
$scope.update = $rootScope.update;
|
||||
|
||||
//Many-to-many Select for Mapping OscarhostFields -> FormFields
|
||||
$scope.oscarFieldsLeft = function(field_id){
|
||||
// LOGIC JUMP METHODS
|
||||
$scope.removeLogicJump = function (field_index) {
|
||||
var currField = $scope.myform.form_fields[field_index];
|
||||
currField.logicJump = {};
|
||||
};
|
||||
|
||||
if($scope.myform && $scope.myform.plugins.oscarhost.settings.validFields.length > 0){
|
||||
if(!$scope.myform.plugins.oscarhost.settings.fieldMap) $scope.myform.plugins.oscarhost.settings.fieldMap = {};
|
||||
$scope.addNewLogicJump = function (field_index) {
|
||||
var form_fields = $scope.myform.form_fields;
|
||||
var currField = form_fields[field_index];
|
||||
console.log(currField);
|
||||
if (form_fields.length > 1 && currField._id) {
|
||||
|
||||
var oscarhostFields = $scope.myform.plugins.oscarhost.settings.validFields;
|
||||
var currentFields = _($scope.myform.plugins.oscarhost.settings.fieldMap).invert().keys().value();
|
||||
|
||||
if( $scope.myform.plugins.oscarhost.settings.fieldMap.hasOwnProperty(field_id) ){
|
||||
currentFields = _(currentFields).difference($scope.myform.plugins.oscarhost.settings.fieldMap[field_id]);
|
||||
}
|
||||
|
||||
//Get all oscarhostFields that haven't been mapped to a formfield
|
||||
return _(oscarhostFields).difference(currentFields).value();
|
||||
}
|
||||
return [];
|
||||
};
|
||||
var newLogicJump = {
|
||||
fieldA: currField._id
|
||||
};
|
||||
currField.logicJump = newLogicJump;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
** FormFields (ui-sortable) drag-and-drop configuration
|
||||
|
@ -109,12 +105,13 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
|||
}
|
||||
}
|
||||
var newField = {
|
||||
title: fieldTitle,
|
||||
title: fieldTitle + ' ' + $scope.myform.form_fields.length+1,
|
||||
fieldType: fieldType,
|
||||
fieldValue: '',
|
||||
required: true,
|
||||
disabled: false,
|
||||
deletePreserved: false
|
||||
deletePreserved: false,
|
||||
logicJump: {}
|
||||
};
|
||||
|
||||
if($scope.showAddOptions(newField)){
|
||||
|
@ -126,7 +123,6 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
if(modifyForm){
|
||||
//Add newField to form_fields array
|
||||
$scope.myform.form_fields.push(newField);
|
||||
|
@ -135,15 +131,11 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
|||
};
|
||||
|
||||
// Delete particular field on button click
|
||||
$scope.deleteField = function (field_index){
|
||||
|
||||
//Delete field from field map
|
||||
var currFieldId = $scope.myform.form_fields[field_index]._id;
|
||||
if($scope.myform.hasOwnProperty('plugins.oscarhost.baseUrl')) delete $scope.myform.plugins.oscarhost.settings.fieldMap[currFieldId];
|
||||
|
||||
//Delete field
|
||||
$scope.deleteField = function (field_index) {
|
||||
console.log($scope.myform.form_fields);
|
||||
$scope.myform.form_fields.splice(field_index, 1);
|
||||
};
|
||||
|
||||
$scope.duplicateField = function (field_index){
|
||||
var currField = _.cloneDeep($scope.myform.form_fields[field_index]);
|
||||
currField._id = 'cloned'+_.uniqueId();
|
||||
|
@ -249,10 +241,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
|||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
|
|
@ -75,7 +75,6 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
|
|||
|
||||
var visitors = $scope.myform.analytics.visitors;
|
||||
|
||||
console.log(visitors);
|
||||
for(var i=0; i<visitors.length; i++){
|
||||
var visitor = visitors[i];
|
||||
var deviceType = visitor.deviceType;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<div class="container" cg-busy="{promise:updatePromise,templateUrl:'modules/forms/admin/views/directiveViews/cgBusy/update-form-message-TypeB.html',message:'Updating form...', backdrop:false, wrapperClass:'.busy-updating-wrapper'}"></div>
|
||||
|
||||
<section class="container admin-form">
|
||||
<section class="admin-form">
|
||||
|
||||
<!-- Modal Delete Dialog Template -->
|
||||
<script type="text/ng-template" id="myModalContent.html">
|
||||
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
</script>
|
||||
|
||||
<div class="page-header row-fluid" style="padding-bottom: 1em;">
|
||||
<div class="page-header" style="padding-bottom: 1em;">
|
||||
<div class="col-xs-10 col-sm-8">
|
||||
<h1 class="hidden-sm hidden-xs" data-ng-bind="myform.title" style="margin-bottom: 0px;"></h1>
|
||||
<h2 class="hidden-md hidden-lg" data-ng-bind="myform.title" style="margin-bottom: 0px;"></h2>
|
||||
|
@ -45,19 +45,19 @@
|
|||
</div>
|
||||
<div class="col-xs-1 col-sm-2">
|
||||
<small class="pull-right">
|
||||
<a class="btn btn-default view-form-btn" href="//{{formURL}}/#!/forms/{{myform._id}}">
|
||||
<a class="btn btn-secondary view-form-btn" href="{{actualFormURL}}">
|
||||
<span class="hidden-xs hidden-sm">
|
||||
{{ 'VIEW' | translate }}
|
||||
<span ng-show="myform.isLive">
|
||||
{{ 'LIVE' | translate }}
|
||||
</span>
|
||||
<span ng-hide="myform.isLive">{{ 'PREVIEW' | translate }}</span> {{ 'FORM' | translate }}
|
||||
</span>
|
||||
<span class="hidden-xs hidden-md hidden-lg">
|
||||
View
|
||||
<span ng-if="myform.isLive">{{ 'LIVE' | translate }}</span>
|
||||
<span ng-if="!myform.isLive">{{ 'PREVIEW' | translate }}</span>
|
||||
<span ng-hide="myform.isLive">{{ 'PREVIEW' | translate }}</span>
|
||||
</span>
|
||||
<!--<span class="hidden-xs hidden-md hidden-lg">-->
|
||||
<!--View-->
|
||||
<!--<span ng-if="myform.isLive">{{ 'LIVE' | translate }}</span>-->
|
||||
<!--<span ng-if="!myform.isLive">{{ 'PREVIEW' | translate }}</span>-->
|
||||
<!--</span>-->
|
||||
<i class="status-light status-light-on fa fa-dot-circle-o" ng-if="myform.isLive"></i>
|
||||
<i class="status-light status-light-off fa fa-dot-circle-o" ng-if="!myform.isLive"></i>
|
||||
</a>
|
||||
|
@ -65,16 +65,133 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<!-- <tabset> -->
|
||||
<tabs data="tabData"></tabs>
|
||||
<!-- </tabset> -->
|
||||
</div>
|
||||
<uib-tabset active="activePill" vertical="true" type="pills">
|
||||
<uib-tab index="0" heading="{{ 'CREATE_TAB' | translate }}">
|
||||
<edit-form-directive myform="myform"></edit-form-directive>
|
||||
</uib-tab>
|
||||
<uib-tab ng-repeat="tab in tabData" index="{{$index}}+1" heading="{{tab.heading}}">
|
||||
<div class='row' data-ng-include="'/static/modules/forms/admin/views/adminTabs/'+tab.templateName+'.html'" onload="form_url = trustSrc(formURL)"></div>
|
||||
</uib-tab>
|
||||
<uib-tab ng-if="tabData" heading="Share" index="{{tabData.length}}">
|
||||
<div class="config-form">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<uib-tabset active="activePill" vertical="true" type="pills">
|
||||
<uib-tab index="0" heading="Share your Form">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
Your TellForm is permanently at this URL
|
||||
</div>
|
||||
<div class="col-sm-8 form-input">
|
||||
<span ngclipboard data-clipboard-target="#copyURL"> <input id="copyURL" ng-value="actualFormURL" class="form-control ng-pristine ng-untouched ng-valid"> </span>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<button class="btn btn btn-secondary view-form-btn" ngclipboard data-clipboard-target="#copyURL">
|
||||
Copy <i class="fa fa-clipboard" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
<uib-tab index="1" heading="Embed your Form">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
Copy and Paste this to add your TellForm to your website
|
||||
</div>
|
||||
<div class="col-sm-8 form-input">
|
||||
<span ngclipboard data-clipboard-target="#copyEmbedded">
|
||||
<textarea id="copyEmbedded" class="form-control ng-pristine ng-untouched ng-valid" style="min-height:200px; width:100%; background-color: #FFFFCC; color: #30313F;">
|
||||
<!-- Change the width and height values to suit you best -->
|
||||
<iframe id="iframe" ng-if="!!formURL" src="{{trustSrc(formURL)}}" style="width:100%;height:500px;"></iframe>
|
||||
<div style="font-family: Sans-Serif;font-size: 12px;color: #999;opacity: 0.5; padding-top: 5px;">Powered by<a href="https://www.tellform.com" style="color: #999" target="_blank">TellForm</a></div>
|
||||
</textarea>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<button class="btn btn btn-secondary view-form-btn" ngclipboard data-clipboard-target="#copyEmbedded">
|
||||
Copy <i class="fa fa-clipboard" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
<uib-tab ng-if="tabData && myform.form_fields.length" heading="Design" index="{{tabData.length}}+1">
|
||||
<div class="config-form design container">
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-sm-12 container">
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-5">
|
||||
<h5>{{ 'BACKGROUND_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
<div class="field-input col-sm-6">
|
||||
<input class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.backgroundColor" ng-style="{ 'background-color': myform.design.colors.backgroundColor }"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12">
|
||||
<ui-view></ui-view>
|
||||
</div>
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-5">
|
||||
<h5>{{ 'QUESTION_TEXT_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-6">
|
||||
<input class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.questionColor" ng-style="{ 'background-color': myform.design.colors.questionColor }"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-5">
|
||||
<h5>{{ 'ANSWER_TEXT_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-6">
|
||||
<input class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.answerColor" ng-style="{ 'background-color': myform.design.colors.answerColor }"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-5">
|
||||
<h5>{{ 'BTN_BACKGROUND_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-6">
|
||||
<input class="form-control" colorpicker="hex" type="text"
|
||||
ng-model="myform.design.colors.buttonColor"
|
||||
ng-style="{ 'background-color': myform.design.colors.buttonColor }"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-5">
|
||||
<h5>{{ 'BTN_TEXT_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-6">
|
||||
<input class="form-control" colorpicker="hex" type="text"
|
||||
ng-model="myform.design.colors.buttonTextColor"
|
||||
ng-style="{ 'background-color': myform.design.colors.buttonTextColor }"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8 hide-md hide-lg">
|
||||
<iframe id="iframe" ng-if="!!formURL" src="{{trustSrc(formURL)}}" style="border: none; box-shadow: 0px 0px 10px 0px grey; overflow: hidden; height: 400px; width: 90%; position: absolute;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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, null)"><i class="icon-arrow-left icon-white"></i>{{ 'SAVE_CHANGES' | translate }}</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
<div class="config-form design container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h2 class="hidden-sm hidden-xs">{{ 'DESIGN_HEADER' | translate }}</h2>
|
||||
<h3 class="hidden-lg hidden-md">{{ 'DESIGN_HEADER' | translate }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-12 container">
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-3">
|
||||
<h5>{{ 'BACKGROUND_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-9">
|
||||
|
||||
<input colorpicker="hex" type="text" ng-model="myform.design.colors.backgroundColor" ng-style="{ 'background-color': myform.design.colors.backgroundColor }"/>
|
||||
<div class="field-input col-sm-6">
|
||||
<input ng-change="refreshIframe()" class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.backgroundColor" ng-style="{ 'background-color': myform.design.colors.backgroundColor }"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -24,11 +15,8 @@
|
|||
<h5>{{ 'QUESTION_TEXT_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-9">
|
||||
|
||||
<input colorpicker="hex" type="text" ng-model="myform.design.colors.questionColor" ng-style="{ 'background-color': myform.design.colors.questionColor }"/>
|
||||
|
||||
|
||||
<div class="field-input col-sm-6">
|
||||
<input ng-change="refreshIframe()" class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.questionColor" ng-style="{ 'background-color': myform.design.colors.questionColor }"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -37,8 +25,8 @@
|
|||
<h5>{{ 'ANSWER_TEXT_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-9">
|
||||
<input colorpicker="hex" type="text" ng-model="myform.design.colors.answerColor" ng-style="{ 'background-color': myform.design.colors.answerColor }"/>
|
||||
<div class="field-input col-sm-6">
|
||||
<input ng-change="refreshIframe()" class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.answerColor" ng-style="{ 'background-color': myform.design.colors.answerColor }"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -47,8 +35,8 @@
|
|||
<h5>{{ 'BTN_BACKGROUND_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-9">
|
||||
<input colorpicker="hex" type="text"
|
||||
<div class="field-input col-sm-6">
|
||||
<input ng-change="refreshIframe()" class="form-control" colorpicker="hex" type="text"
|
||||
ng-model="myform.design.colors.buttonColor"
|
||||
ng-style="{ 'background-color': myform.design.colors.buttonColor }"/>
|
||||
</div>
|
||||
|
@ -58,21 +46,24 @@
|
|||
<h5>{{ 'BTN_TEXT_COLOR' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-9">
|
||||
<input colorpicker="hex" type="text"
|
||||
<div class="field-input col-sm-6">
|
||||
<input ng-change="refreshIframe()" class="form-control" colorpicker="hex" type="text"
|
||||
ng-model="myform.design.colors.buttonTextColor"
|
||||
ng-style="{ 'background-color': myform.design.colors.buttonTextColor }"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8 hide-md hide-lg">
|
||||
<iframe refreshable="refreshDesign" ng-if="!!form_url" src="{{form_url}}" style="border: none; box-shadow: 0px 0px 10px 0px grey; overflow: hidden; height: 95vh; width: 90%; position: absolute;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-4 col-sm-2">
|
||||
<button class="btn btn-primary btn-large" type="button" ng-click="update(false, null)"><i class="icon-arrow-left icon-white"></i> Save Changes</button>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<button class="btn btn-default" type="button" ng-click="resetForm()"><i class="icon-eye-open icon-white"></i> Cancel</button>
|
||||
</div>
|
||||
<div class="col-sm-offset-4 col-sm-2">
|
||||
<button class="btn btn-signup btn-rounded" type="button" ng-click="update(false, null)"><i class="icon-arrow-left icon-white"></i>{{ 'SAVE_CHANGES' | translate }}</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
<div class="config-form container">
|
||||
<div class="row">
|
||||
|
||||
<!-- Settings -->
|
||||
<div class="col-sm-12 container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h2 class="hidden-sm hidden-xs">{{ 'ADVANCED_SETTINGS' | translate }}</h2>
|
||||
<h3 class="hidden-lg hidden-md">{{ 'ADVANCED_SETTINGS' | translate }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--<div class="row">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<h2 class="hidden-sm hidden-xs">{{ 'ADVANCED_SETTINGS' | translate }}</h2>
|
||||
<h3 class="hidden-lg hidden-md">{{ 'ADVANCED_SETTINGS' | translate }}</h3>
|
||||
</div>
|
||||
</div>-->
|
||||
<!-- Settings -->
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-2 col-sm-4">
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-4">
|
||||
<div class="field-title col-sm-12">
|
||||
<h5>{{ 'FORM_NAME' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<input type="text"
|
||||
<div class="col-sm-12">
|
||||
<input class="form-control"
|
||||
type="text"
|
||||
ng-model="myform.title"
|
||||
value="{{myform.title}}"
|
||||
style="width: 100%;"
|
||||
|
@ -26,18 +25,18 @@
|
|||
</div>
|
||||
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-6">
|
||||
<div class="field-title col-sm-12">
|
||||
<h5>{{ 'FORM_STATUS' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-6">
|
||||
<div class="field-input col-sm-12">
|
||||
|
||||
<label>
|
||||
<label style="display: inline-block;">
|
||||
<input type="radio" data-ng-value="true" ng-model="myform.isLive" ng-required="true" style="background-color:#33CC00;"/>
|
||||
<span>{{ 'PUBLIC' | translate }}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<label style="display: inline-block;">
|
||||
<input type="radio" data-ng-value="false" ng-model="myform.isLive" ng-required="true" />
|
||||
<span>{{ 'PRIVATE' | translate }}</span>
|
||||
</label>
|
||||
|
@ -45,25 +44,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-4">
|
||||
<h5>{{ 'GA_TRACKING_CODE' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<input type="text"
|
||||
ng-model="myform.analytics.gaCode"
|
||||
value="{{myform.analytics.gaCode}}"
|
||||
style="width: 100%;"
|
||||
ng-minlength="4"
|
||||
placeholder="UA-XXXXX-Y"
|
||||
ng-pattern="/\bUA-\d{4,10}-\d{1,4}\b/">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row field">
|
||||
<div class="col-xs-6 field-title">Language</div>
|
||||
<div class="col-xs-4 field-input">
|
||||
<div class="col-sm-12 field-title">Language</div>
|
||||
<div class="col-sm-12 field-input">
|
||||
<select ng-model="myform.language">
|
||||
<option ng-repeat="language in languages"
|
||||
ng-selected="language == myform.language"
|
||||
|
@ -74,51 +57,69 @@
|
|||
<span class="required-error" ng-show="field.required && !field.fieldValue">* required</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-6">
|
||||
<h5>{{ 'DISPLAY_FOOTER' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-6">
|
||||
<label>
|
||||
<input type="radio" data-ng-value="false" ng-model="myform.hideFooter" ng-required="true" />
|
||||
<span>{{ 'YES' | translate }}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="radio" data-ng-value="true" ng-model="myform.hideFooter" ng-required="true" />
|
||||
<span>{{ 'No' | translate }}</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;"/>
|
||||
<span>{{ 'YES' | translate }}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="radio" data-ng-value="false" ng-model="myform.startPage.showStart" ng-required="true" />
|
||||
<span>{{ 'NO' | translate }}</span>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-12">
|
||||
<h5>{{ 'GA_TRACKING_CODE' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<input class="form-control"
|
||||
type="text"
|
||||
ng-model="myform.analytics.gaCode"
|
||||
value="{{myform.analytics.gaCode}}"
|
||||
style="width: 100%;"
|
||||
ng-minlength="4"
|
||||
placeholder="UA-XXXXX-Y"
|
||||
ng-pattern="/\bUA-\d{4,10}-\d{1,4}\b/">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-12">
|
||||
<h5>{{ 'DISPLAY_FOOTER' | translate }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-12">
|
||||
<label style="display: inline-block;">
|
||||
<input type="radio" data-ng-value="false" ng-model="myform.hideFooter" ng-required="true" />
|
||||
<span>{{ 'YES' | translate }}</span>
|
||||
</label>
|
||||
|
||||
<label style="display: inline-block;">
|
||||
<input type="radio" data-ng-value="true" ng-model="myform.hideFooter" ng-required="true" />
|
||||
<span>{{ 'No' | translate }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row field">
|
||||
<div class="field-title col-sm-12">
|
||||
<h5>Display Start Page?</h5>
|
||||
</div>
|
||||
|
||||
<div class="field-input col-sm-12">
|
||||
|
||||
<label style="display: inline-block;">
|
||||
<input type="radio" data-ng-value="true" ng-model="myform.startPage.showStart" ng-required="true" style="background-color:#33CC00;"/>
|
||||
<span>{{ 'YES' | translate }}</span>
|
||||
</label>
|
||||
|
||||
<label style="display: inline-block;">
|
||||
<input type="radio" data-ng-value="false" ng-model="myform.startPage.showStart" ng-required="true" />
|
||||
<span>{{ 'NO' | translate }}</span>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-4 col-sm-2">
|
||||
<button class="btn btn-primary btn-large" type="button" ng-click="update(false, null)"><i class="icon-arrow-left icon-white"></i>{{ 'SAVE_CHANGES' | translate }}</button>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<button class="btn btn-default" type="button" ng-click="resetForm()"><i class="icon-eye-open icon-white"></i>{{ 'CANCEL' | translate }}</button>
|
||||
</div>
|
||||
<div class="col-sm-offset-4 col-sm-2">
|
||||
<button class="btn btn-signup btn-rounded" type="button" ng-click="update(false, null)"><i class="icon-arrow-left icon-white"></i>{{ 'SAVE_CHANGES' | translate }}</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<form class="row container" name="editForm" auto-save-form auto-save-watch="myform" auto-save-callback="update">
|
||||
|
||||
<!-- Add Fields Element -->
|
||||
<div class="col-xs-2 col-sm-4 col-md-4 add-field">
|
||||
<div class="col-xs-2 col-sm-4 add-field">
|
||||
|
||||
<div class="row add-field-title">
|
||||
<h3 class="col-md-12 hidden-sm hidden-xs">{{ 'ADD_FIELD_LG' | translate }}</h3>
|
||||
|
@ -249,7 +249,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row" ng-show="showRatingOptions(field)"><br></div>
|
||||
<div class="row" ng-if="showRatingOptions(field)">
|
||||
<div class="col-md-9 col-sm-9">{{ 'NUM_OF_STEPS' | translate }}</div>
|
||||
|
@ -293,7 +292,7 @@
|
|||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-xs-12 field-input">Disabled:</div>
|
||||
<div class="col-md-4 col-xs-12 field-input">{{ 'DISABLED' | translate }}</div>
|
||||
<div class="col-md-8 col-xs-12 field-input">
|
||||
<label class="btn col-xs-5">
|
||||
<input type="radio" ng-value="true"
|
||||
|
@ -309,6 +308,106 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="myform.form_fields.length > 1">
|
||||
<h4> Logic Jump </h4>
|
||||
</div>
|
||||
<div class="row" ng-if="myform.form_fields.length > 1">
|
||||
<div class="col-md-4 col-xs-12 field-input">{{ 'ADD_LOGIC_JUMP' | translate }}</div>
|
||||
<div class="col-md-8 col-xs-12 field-input">
|
||||
<label class="btn col-xs-5">
|
||||
<input type="radio" ng-checked="!!myform.form_fields[$index].logicJump.fieldA"
|
||||
name="logicJumpYes{{field._id}}" ng-click="addNewLogicJump($index)"/>
|
||||
<span> {{ 'YES' | translate }}</span>
|
||||
</label>
|
||||
|
||||
<label class="btn col-xs-5 col-xs-offset-1">
|
||||
<input type="radio" ng-checked="!myform.form_fields[$index].logicJump.fieldA"
|
||||
name="logicJumpNo{{field._id}}" ng-click="removeLogicJump($index)"/>
|
||||
<span> {{ 'NO' | translate }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row question" ng-if="myform.form_fields.length > 1 && myform.form_fields[$index].logicJump.fieldA">
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<!-- FIX ME: translate this -->
|
||||
<b> If this field </b>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<select style="width:100%" ng-model="field.logicJump.expressionString"
|
||||
value="{{field.logicJump.expressionString}}"
|
||||
name="logicjump_expressionString{{field._id}}">
|
||||
<option value="field == static">
|
||||
<!-- FIX ME: translate this -->
|
||||
is equal to
|
||||
</option>
|
||||
<option value="field != static">
|
||||
<!-- FIX ME: translate this -->
|
||||
is not equal to
|
||||
</option>
|
||||
|
||||
<option value="field > static" ng-if-start="field.fieldType === 'number' || field.fieldType === 'rating' || field.fieldType === 'number'">
|
||||
<!-- FIX ME: translate this -->
|
||||
is greater than
|
||||
</option>
|
||||
<option value="field >= static">
|
||||
<!-- FIX ME: translate this -->
|
||||
is greater or equal than
|
||||
</option>
|
||||
<option value="field < static">
|
||||
<!-- FIX ME: translate this -->
|
||||
is smaller than
|
||||
</option>
|
||||
<option value="field <= static" ng-if-end>
|
||||
<!-- FIX ME: translate this -->
|
||||
is smaller or equal than
|
||||
</option>
|
||||
|
||||
<option value="field contains static" ng-if-start="field.fieldType !== 'number' && field.fieldType !== 'rating' && field.fieldType !== 'number'">
|
||||
<!-- FIX ME: translate this -->
|
||||
contains
|
||||
</option>
|
||||
<option value="field !contains static">
|
||||
<!-- FIX ME: translate this -->
|
||||
does not contain
|
||||
</option>
|
||||
<option value="field ends static">
|
||||
<!-- FIX ME: translate this -->
|
||||
ends with
|
||||
</option>
|
||||
<option value="field !ends static">
|
||||
<!-- FIX ME: translate this -->
|
||||
does not end with
|
||||
</option>
|
||||
<option value="field starts static">
|
||||
<!-- FIX ME: translate this -->
|
||||
starts with
|
||||
</option>
|
||||
<option value="field !starts static" ng-if-end>
|
||||
<!-- FIX ME: translate this -->
|
||||
does not start with
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<input type="text" ng-model="field.logicJump.valueB"/>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<!-- FIX ME: translate this -->
|
||||
<b>Jumps to </b>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<select style="width:100%" ng-model="field.logicJump.jumpTo"
|
||||
value="{{field.logicJump.jumpTo}}"
|
||||
name="logicjump_jumpTo{{field._id}}">
|
||||
<option ng-repeat="jump_field in myform.form_fields"
|
||||
value="{{jump_field._id}}">
|
||||
{{jump_field.title}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</accordion-group>
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<div class="submissions-table row container">
|
||||
<div class="submissions-table container">
|
||||
<div class="row text-center analytics">
|
||||
<div class="row col-xs-12 header-numbers">
|
||||
Overview Analytics
|
||||
</div>
|
||||
<div class="row col-xs-12 header-title">
|
||||
<div class="col-xs-12 header-title">
|
||||
<div class="col-xs-3">
|
||||
{{ 'TOTAL_VIEWS' | translate }}
|
||||
</div>
|
||||
|
@ -20,7 +17,7 @@
|
|||
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row col-xs-12 header-numbers">
|
||||
<div class="col-xs-12 header-numbers">
|
||||
<div class="col-xs-3">
|
||||
{{myform.analytics.views}}
|
||||
</div>
|
||||
|
@ -37,10 +34,7 @@
|
|||
{{AverageTimeElapsed | secondsToDateTime | date:'mm:ss'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row col-xs-12 header-numbers">
|
||||
Device Analytics
|
||||
</div>
|
||||
<div class="row col-xs-12 detailed-title">
|
||||
<div class="col-xs-12 detailed-title">
|
||||
<div class="col-xs-3">
|
||||
{{ 'DESKTOP_AND_LAPTOP' | translate }}
|
||||
</div>
|
||||
|
@ -58,7 +52,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row col-xs-12 detailed-row">
|
||||
<div class="col-xs-12 detailed-row">
|
||||
<div class="col-xs-3">
|
||||
<div class="row header">
|
||||
{{ 'UNIQUE_VISITS' | translate }}
|
||||
|
@ -96,7 +90,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row col-xs-12 detailed-row">
|
||||
<div class="col-xs-12 detailed-row">
|
||||
<div class="col-xs-3">
|
||||
<div class="row header">
|
||||
{{ 'RESPONSES' | translate }}
|
||||
|
@ -134,7 +128,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row col-xs-12 detailed-row">
|
||||
<div class="col-xs-12 detailed-row">
|
||||
<div class="col-xs-3">
|
||||
<div class="row header">
|
||||
{{ 'COMPLETION_RATE' | translate }}
|
||||
|
@ -172,7 +166,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row col-xs-12 detailed-row">
|
||||
<div class="col-xs-12 detailed-row">
|
||||
<div class="col-xs-3">
|
||||
<div class="row header">
|
||||
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
|
||||
|
@ -210,10 +204,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row col-xs-12 text-center" style="font-size:5em;">
|
||||
Field Analytics
|
||||
</div>
|
||||
<div class="row col-xs-12 field-title-row">
|
||||
<div class="col-xs-12 field-title-row">
|
||||
|
||||
<div class="col-xs-3">
|
||||
<strong>{{ 'FIELD_TITLE' | translate }}</strong>
|
||||
|
@ -230,7 +221,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row col-xs-12 field-detailed-row" ng-repeat="fieldStats in myform.analytics.fields">
|
||||
<div class="col-xs-12 field-detailed-row" ng-repeat="fieldStats in myform.analytics.fields">
|
||||
|
||||
<div class="col-xs-3">
|
||||
{{fieldStats.field.title}}
|
||||
|
@ -247,9 +238,6 @@
|
|||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="row col-xs-12 header-numbers text-center" style="font-size:5em;">
|
||||
Responses Table
|
||||
</div>
|
||||
<div class="row table-tools">
|
||||
<div class="col-xs-2">
|
||||
<button class="btn btn-danger" ng-click="deleteSelectedSubmissions()" ng-disabled="!isAtLeastOneChecked();">
|
||||
|
|
|
@ -4,6 +4,13 @@
|
|||
|
||||
<section data-ng-controller="ListFormsController as ctrl" data-ng-init="findAll()" class="container">
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="col-xs-4 col-xs-offset-4">
|
||||
<h3 class="text-center forms-list-title">
|
||||
My Forms
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div ng-click="openCreateModal()" 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">
|
||||
<div class="title-row col-xs-12">
|
||||
|
@ -47,8 +54,8 @@
|
|||
</form>
|
||||
|
||||
<div data-ng-repeat="form in myforms"
|
||||
ng-style="{ 'background-color': form.design.colors.backgroundColor, 'color': form.design.colors.answerColor }"
|
||||
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">
|
||||
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"
|
||||
ng-class="{'paused': !form.isLive}">
|
||||
|
||||
<div class="row">
|
||||
<span class="pull-right">
|
||||
|
@ -58,21 +65,19 @@
|
|||
</div>
|
||||
|
||||
<div class="row">
|
||||
<a data-ng-href="#!/forms/{{form._id}}/admin/create"
|
||||
ng-style="{ 'color': form.design.colors.answerColor }"
|
||||
class="title-row col-xs-12">
|
||||
<h4 class="list-group-item-heading" data-ng-bind="form.title"></h4>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="row footer">
|
||||
<div class="col-xs-12 details-row">
|
||||
|
||||
<a data-ng-href="#!/forms/{{form._id}}/admin/create"
|
||||
class="title-row col-xs-12">
|
||||
<h4 class="list-group-item-heading" data-ng-bind="form.title"></h4>
|
||||
</a>
|
||||
<div class="col-xs-12 responses-row">
|
||||
<small class="list-group-item-text">
|
||||
{{ 'CREATED_ON' | translate }}
|
||||
<span data-ng-bind="form.created | date:'shortDate'"></span>
|
||||
<span> {{ form.submissions.length }} responses </span>
|
||||
</small>
|
||||
<br>
|
||||
<br>
|
||||
<small ng-if="!form.isLive" class="list-group-item-text">
|
||||
<span> Form Paused </span>
|
||||
</small>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
0
public/modules/forms/base/bower.json
Executable file → Normal file
0
public/modules/forms/base/bower.json
Executable file → Normal file
|
@ -1,3 +1,19 @@
|
|||
/* Custom Tab CSS */
|
||||
.nav.nav-pills.nav-stacked {
|
||||
width: 16.66666667%;
|
||||
float: left;
|
||||
position: relative;
|
||||
min-height: 1px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
div.tab-content {
|
||||
width: 83.33333333%;
|
||||
position: relative;
|
||||
min-height: 1px;
|
||||
float:left;
|
||||
padding-top: 0!important;
|
||||
}
|
||||
|
||||
.panel-default.startPage {
|
||||
border-style: dashed;
|
||||
border-color: #a9a9a9;
|
||||
|
@ -302,6 +318,8 @@ div.config-form .row.field {
|
|||
}
|
||||
.admin-form .page-header {
|
||||
border: none;
|
||||
margin-top: none;
|
||||
margin-bottom: none;
|
||||
}
|
||||
|
||||
/*Styles for admin view tabs */
|
||||
|
@ -419,16 +437,26 @@ section.public-form .btn {
|
|||
}
|
||||
|
||||
.form-item {
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
border-bottom: 6px inset #ccc;
|
||||
background-color: #eee;
|
||||
width: 180px;
|
||||
/*width:100%;*/
|
||||
position: relative;
|
||||
height: 215px;
|
||||
/*padding-bottom: 25%;*/
|
||||
margin-bottom: 45px;
|
||||
}
|
||||
|
||||
|
||||
.form-item.paused {
|
||||
background-color: red;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.form-item.paused:hover {
|
||||
background-color: darkred;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.form-item.create-new input[type='text']{
|
||||
width: inherit;
|
||||
color:black;
|
||||
|
@ -436,17 +464,22 @@ section.public-form .btn {
|
|||
}
|
||||
|
||||
.form-item.create-new {
|
||||
background-color: rgb(131,131,131);
|
||||
background-color: #3FA2F7;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.form-item.create-new:hover {
|
||||
background-color: #276496;
|
||||
}
|
||||
|
||||
/*CREATE-NEW FORM MODAL*/
|
||||
.form-item.create-new.new-form {
|
||||
background-color: rgb(300,131,131);
|
||||
z-index: 11;
|
||||
}
|
||||
.form-item.create-new.new-form:hover {
|
||||
background-color: rgb(300,100,100);
|
||||
background-color: #3079b5;
|
||||
}
|
||||
.form-item.new-form input[type='text'] {
|
||||
margin-top:0.2em;
|
||||
|
@ -513,10 +546,23 @@ section.public-form .btn {
|
|||
.activeField input {
|
||||
background-color: transparent;
|
||||
}
|
||||
h3.forms-list-title {
|
||||
color: #3FA2F7;
|
||||
weight: 600;
|
||||
margin-bottom: 3em;
|
||||
}
|
||||
.form-item {
|
||||
color: #71AADD;
|
||||
background-color: #E4F1FD;
|
||||
}
|
||||
.form-item:hover {
|
||||
background-color: #3FA2F7;
|
||||
color: #23527C;
|
||||
}
|
||||
|
||||
.form-item:hover, .form-item.create-new:hover {
|
||||
border-bottom: 8px inset #ccc;
|
||||
.form-item.create-new:hover {
|
||||
background-color: #d9d9d9;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.form-item.create-new:hover {
|
||||
|
@ -529,15 +575,17 @@ section.public-form .btn {
|
|||
left: 30%;
|
||||
}
|
||||
|
||||
.form-item .title-row{
|
||||
.form-item .title-row {
|
||||
position: relative;
|
||||
top: 15px;
|
||||
padding-top:3em;
|
||||
padding-bottom:3.65em;
|
||||
padding-bottom:1em;
|
||||
}
|
||||
.form-item .title-row h4 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.form-item .responses-row {
|
||||
}
|
||||
|
||||
.form-item.create-new .title-row{
|
||||
padding: 0;
|
||||
|
|
|
@ -83,23 +83,24 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
|
|||
if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){
|
||||
switch(scope.field.fieldType){
|
||||
case 'textfield':
|
||||
scope.field.input_type = 'text';
|
||||
scope.input_type = 'text';
|
||||
break;
|
||||
case 'email':
|
||||
scope.field.input_type = 'email';
|
||||
scope.field.placeholder = 'joesmith@example.com';
|
||||
scope.input_type = 'email';
|
||||
scope.placeholder = 'joesmith@example.com';
|
||||
break;
|
||||
case 'number':
|
||||
scope.field.input_type = 'text';
|
||||
scope.field.validateRegex = /^-?\d+$/;
|
||||
scope.input_type = 'text';
|
||||
scope.validateRegex = /^-?\d+$/;
|
||||
break;
|
||||
default:
|
||||
scope.field.input_type = 'url';
|
||||
scope.field.placeholder = 'http://example.com';
|
||||
scope.input_type = 'url';
|
||||
scope.placeholder = 'http://example.com';
|
||||
break;
|
||||
}
|
||||
fieldType = 'textfield';
|
||||
}
|
||||
|
||||
var template = getTemplateUrl(fieldType);
|
||||
element.html(template).show();
|
||||
var output = $compile(element.contents())(scope);
|
||||
|
|
0
public/modules/forms/base/views/directiveViews/field/date.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/field/date.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/field/dropdown.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/field/dropdown.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/field/hidden.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/field/hidden.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/field/radio.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/field/radio.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/field/textarea.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/field/textarea.html
Executable file → Normal file
86
public/modules/forms/base/views/directiveViews/field/textfield.html
Executable file → Normal file
86
public/modules/forms/base/views/directiveViews/field/textfield.html
Executable file → Normal file
|
@ -1,8 +1,8 @@
|
|||
<div class="textfield field row"
|
||||
ng-click="setActiveField(field._id, index, true)">
|
||||
<div class="col-xs-12 field-title row-fluid" ng-style="{'color': design.colors.questionColor}">
|
||||
<h3 class="col-xs-12">
|
||||
<small class="field-number">
|
||||
<h3 class="col-xs-12">
|
||||
<small class="field-number">
|
||||
{{index+1}}
|
||||
<i class="fa fa-angle-double-right" aria-hidden="true"></i>
|
||||
</small>
|
||||
|
@ -12,54 +12,54 @@
|
|||
<span class="required-error" ng-show="!field.required">
|
||||
({{ 'OPTIONAL' | translate }})
|
||||
</span>
|
||||
</h3>
|
||||
</h3>
|
||||
|
||||
<p class="col-xs-12">
|
||||
<small>{{field.description}}</small>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-xs-12 field-input">
|
||||
</div>
|
||||
<div class="col-xs-12 field-input">
|
||||
<input ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}"
|
||||
name="{{field.fieldType}}{{index}}"
|
||||
type="{{field.input_type}}"
|
||||
ng-pattern="field.validateRegex"
|
||||
placeholder="{{field.placeholder}}"
|
||||
ng-class="{ 'no-border': !!field.fieldValue }"
|
||||
class="focusOn text-field-input"
|
||||
ng-model="field.fieldValue"
|
||||
ng-model-options="{ debounce: 250 }"
|
||||
value="field.fieldValue"
|
||||
ng-focus="setActiveField(field._id, index, true)"
|
||||
on-enter-or-tab-key="nextField()"
|
||||
on-tab-and-shift-key="prevField()"
|
||||
ng-required="field.required"
|
||||
ng-disabled="field.disabled"
|
||||
aria-describedby="inputError2Status">
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<div ng-show="forms.myForm.{{field.fieldType}}{{index}}.$invalid && !!forms.myForm.{{field.fieldType}}{{index}}.$viewValue " class="alert alert-danger" role="alert">
|
||||
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
|
||||
<span class="sr-only">Error:</span>
|
||||
name="{{field.fieldType}}{{index}}"
|
||||
type="{{input_type}}"
|
||||
ng-pattern="validateRegex"
|
||||
placeholder="{{placeholder}}"
|
||||
ng-class="{ 'no-border': !!field.fieldValue }"
|
||||
class="focusOn text-field-input"
|
||||
ng-model="field.fieldValue"
|
||||
ng-model-options="{ debounce: 250 }"
|
||||
value="field.fieldValue"
|
||||
ng-focus="setActiveField(field._id, index, true)"
|
||||
on-enter-or-tab-key="nextField()"
|
||||
on-tab-and-shift-key="prevField()"
|
||||
ng-required="field.required"
|
||||
ng-disabled="field.disabled"
|
||||
aria-describedby="inputError2Status">
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<div ng-show="forms.myForm.{{field.fieldType}}{{index}}.$invalid && !!forms.myForm.{{field.fieldType}}{{index}}.$viewValue " class="alert alert-danger" role="alert">
|
||||
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
|
||||
<span class="sr-only">Error:</span>
|
||||
<span ng-if="field.fieldType == 'email'"> {{ 'ERROR_EMAIL_INVALID' | translate }} </span>
|
||||
<span ng-if="field.validateRegex"> {{ 'ERROR_NOT_A_NUMBER' | translate }} </span>
|
||||
<span ng-if="field.fieldType == 'link'"> {{ 'ERROR_URL_INVALID' | translate }} </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="btn btn-lg btn-default hidden-xs"
|
||||
style="padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)">
|
||||
<button ng-disabled="!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid"
|
||||
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
|
||||
ng-click="$root.nextField()"
|
||||
class="btn col-sm-5 col-xs-5">
|
||||
|
||||
{{ 'OK' | translate }} <i class="fa fa-check"></i>
|
||||
</button>
|
||||
<div class="col-xs-6 col-sm-3" style="margin-top:0.2em">
|
||||
<small style="color:#ddd; font-size:70%">
|
||||
{{ 'ENTER' | translate }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="btn btn-lg btn-default col-xs-12 col-sm-4 hidden-xs"
|
||||
style="padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)">
|
||||
<button ng-disabled="!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid"
|
||||
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
|
||||
ng-click="$root.nextField()"
|
||||
class="btn col-sm-5 col-xs-5">
|
||||
|
||||
{{ 'OK' | translate }} <i class="fa fa-check"></i>
|
||||
</button>
|
||||
<div class="col-xs-6 col-sm-3" style="margin-top:0.2em">
|
||||
<small style="color:#ddd; font-size:70%">
|
||||
{{ 'ENTER' | translate }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
0
public/modules/forms/base/views/directiveViews/form/submit-form.client.view.html
Executable file → Normal file
0
public/modules/forms/base/views/directiveViews/form/submit-form.client.view.html
Executable file → Normal file
|
@ -13,7 +13,7 @@
|
|||
// Create a controller method for sending visitor data
|
||||
function send(form, lastActiveIndex, timeElapsed) {
|
||||
// Create a new message object
|
||||
var visitorData = {
|
||||
/*var visitorData = {
|
||||
referrer: document.referrer,
|
||||
isSubmitted: form.submitted,
|
||||
formId: form._id,
|
||||
|
@ -42,15 +42,15 @@
|
|||
}
|
||||
console.log(visitorData.deviceType);
|
||||
Socket.emit('form-visitor-data', visitorData);
|
||||
});
|
||||
});*/
|
||||
|
||||
}
|
||||
|
||||
function init(){
|
||||
// Make sure the Socket is connected
|
||||
if (!Socket.socket) {
|
||||
/*if (!Socket.socket) {
|
||||
Socket.connect();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
var service = {
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
// Use Application configuration module to register a new module
|
||||
ApplicationConfiguration.registerModule('forms', [
|
||||
'ngFileUpload', 'ui.router.tabs', 'ui.date', 'ui.sortable',
|
||||
'angular-input-stars', 'users'
|
||||
'angular-input-stars', 'users', 'ngclipboard'
|
||||
]);//, 'colorpicker.module' @TODO reactivate this module
|
||||
|
|
|
@ -4,7 +4,8 @@ angular.module('users').config(['$translateProvider', function ($translateProvid
|
|||
|
||||
$translateProvider.translations('en', {
|
||||
ACCESS_DENIED_TEXT: 'You need to be logged in to access this page',
|
||||
USERNAME_LABEL: 'Username or Email',
|
||||
USERNAME_OR_EMAIL_LABEL: 'Username or Email',
|
||||
USERNAME_LABEL: 'Username',
|
||||
PASSWORD_LABEL: 'Password',
|
||||
CURRENT_PASSWORD_LABEL: 'Current Password',
|
||||
NEW_PASSWORD_LABEL: 'New Password',
|
||||
|
@ -21,6 +22,9 @@ angular.module('users').config(['$translateProvider', function ($translateProvid
|
|||
SIGNIN_HEADER_TEXT: 'Sign in',
|
||||
|
||||
SIGNUP_ERROR_TEXT: 'Couldn\'t complete registration due to errors',
|
||||
ENTER_ACCOUNT_EMAIL: 'Enter your account email.',
|
||||
RESEND_VERIFICATION_EMAIL: 'Resend Verification Email',
|
||||
SAVE_CHANGES: 'Save Changes',
|
||||
|
||||
UPDATE_PROFILE_BTN: 'Update Profile',
|
||||
PROFILE_SAVE_SUCCESS: 'Profile saved successfully',
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
angular.module('users').controller('PasswordController', ['$scope', '$stateParams', '$state', 'User',
|
||||
function($scope, $stateParams, $state, User) {
|
||||
|
||||
$scope.error = '';
|
||||
|
||||
// Submit forgotten password account id
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('users').controller('SettingsController', ['$scope', '$rootScope', '$http', '$state', 'Users',
|
||||
function($scope, $rootScope, $http, $state, Users) {
|
||||
$scope.user = $rootScope.user;
|
||||
angular.module('users').controller('SettingsController', ['$scope', '$rootScope', '$http', '$state', 'Users', 'Auth',
|
||||
function($scope, $rootScope, $http, $state, Users, Auth) {
|
||||
|
||||
$scope.user = Auth.currentUser;
|
||||
|
||||
// Check if there are additional accounts
|
||||
$scope.hasConnectedAdditionalSocialAccounts = function(provider) {
|
||||
|
@ -12,6 +13,10 @@ angular.module('users').controller('SettingsController', ['$scope', '$rootScope'
|
|||
return false;
|
||||
};
|
||||
|
||||
$scope.cancel = function(){
|
||||
$scope.user = Auth.currentUser;
|
||||
};
|
||||
|
||||
// Check if provider is already in use with current user
|
||||
$scope.isConnectedSocialAccount = function(provider) {
|
||||
return $scope.user.provider === provider || ($scope.user.additionalProvidersData && $scope.user.additionalProvidersData[provider]);
|
||||
|
|
|
@ -6,7 +6,7 @@ section.auth {
|
|||
left: 0;
|
||||
width: 100%;
|
||||
color: white;
|
||||
background-color: #1e5799; /* Old browsers */
|
||||
background-color: #50B5C1; /* Old browsers */
|
||||
background: -moz-linear-gradient(137deg, #50B5C1 0%, #6450A0 85%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(137deg, #50B5C1 0%, #6450A0 85%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(137deg, #50B5C1 0%, #6450A0 85%);
|
||||
|
@ -63,18 +63,31 @@ section.auth {
|
|||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
section.auth .btn-signup {
|
||||
|
||||
.btn-rounded.btn-signup {
|
||||
background-color: #FFD747;
|
||||
color: #896D0B;
|
||||
border: 2px #FFD747 solid;
|
||||
width: 100%;
|
||||
}
|
||||
section.auth .btn-signup:hover {
|
||||
|
||||
.btn-rounded.btn-signup:hover {
|
||||
color: #FFD747;
|
||||
background-color: #896D0B;
|
||||
border: 2px #896D0B solid;
|
||||
}
|
||||
|
||||
.btn-rounded.btn-default {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: 2px white solid;
|
||||
}
|
||||
|
||||
.btn-rounded.btn-default:focus, .btn-rounded.btn-default:hover {
|
||||
color: #6450A0;
|
||||
background-color: white;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.nav-users {
|
||||
position: fixed;
|
||||
|
@ -90,10 +103,12 @@ section.auth {
|
|||
position: absolute;
|
||||
}
|
||||
|
||||
input.form-control {
|
||||
section.auth input.form-control {
|
||||
border: none;
|
||||
}
|
||||
|
||||
input.form-control {
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
box-shadow: none;
|
||||
font-size: 18px;
|
||||
padding: 30px 20px;
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="col-md-12 text-center" style="padding-bottom: 50px;">
|
||||
<img src="/static/modules/core/img/logo_white.svg" height="100px">
|
||||
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<form class="signin form-horizontal" autocomplete="off">
|
||||
|
@ -33,14 +32,14 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<!--<label for="username">{{ 'USERNAME_LABEL' | translate }}</label>-->
|
||||
<input type="text" id="username" name="username" class="form-control" data-ng-model="credentials.username" placeholder="{{ 'USERNAME_LABEL' | translate }}" ng-minlength="4">
|
||||
<input type="text" id="username" name="username" class="form-control" data-ng-model="credentials.username" placeholder="{{ 'USERNAME_OR_EMAIL_LABEL' | translate }}" ng-minlength="4">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<!--<label for="password">{{ 'PASSWORD_LABEL' | translate }}</label>-->
|
||||
<input type="password" id="password" name="password" class="form-control" data-ng-model="credentials.password" placeholder="{{ 'PASSWORD_LABEL' | translate }}" ng-minlength="4">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-signup" ng-click="signin()">{{ 'SIGNIN_BTN' | translate }}</button>
|
||||
<button class="btn btn-signup btn-rounded btn-block" ng-click="signin()">{{ 'SIGNIN_BTN' | translate }}</button>
|
||||
</div>
|
||||
<div class="text-center forgot-password">
|
||||
<a ui-sref="forgot">{{ 'FORGOT_PASSWORD_LINK' | translate }}</a>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<p>
|
||||
<strong>{{ 'BEFORE_YOU_CONTINUE' | translate }}</strong> <a href="mail:polydaic@gmail.com">polydaic@gmail.com</a></p>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-large btn-primary">
|
||||
<button type="submit" class="btn btn-primary btn-rounded">
|
||||
<a href="/#!/" style="color: white; text-decoration: none;">Continue</a>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -30,29 +30,6 @@
|
|||
{{'SIGNUP_ERROR_TEXT' | translate}}: <br>
|
||||
<strong data-ng-bind="error"></strong>
|
||||
</div>
|
||||
<!--<div class="form-group">
|
||||
<label for="firstName">{{ 'FIRST_NAME_LABEL' | translate }}</label>
|
||||
<input type="text" ng-pattern="/^[a-zA-Z0-9 \-.]*$/" required id="firstName" name="firstName" class="form-control" ng-model="credentials.firstName" placeholder="First Name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lastName">{{ 'LAST_NAME_LABEL' | translate }}</label>
|
||||
<input type="text" ng-pattern="/^[a-zA-Z0-9 \-.]*$/" required id="lastName" name="lastName" class="form-control" ng-model="credentials.lastName" placeholder="Last Name">
|
||||
</div>-->
|
||||
<!--<div class="row form-group">
|
||||
<div class="field-title">
|
||||
<b>{{ 'LANGUAGE_LABEL' | translate }}</b>
|
||||
</div>
|
||||
<div class="field-input">
|
||||
<select ng-model="user.language" required>
|
||||
<option ng-repeat="language in languages"
|
||||
ng-selected="language == user.language"
|
||||
value="{{language}}">
|
||||
{{language}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<hr>-->
|
||||
<div class="form-group">
|
||||
<!--<label for="username">{{ 'USERNAME_LABEL' | translate }}</label>-->
|
||||
<input type="text" id="username" name="username" class="form-control" ng-pattern="languageRegExp" ng-minlength="4" ng-model="credentials.username" placeholder="{{ 'USERNAME_LABEL' | translate }}" ng-minlength="4">
|
||||
|
@ -66,7 +43,7 @@
|
|||
<input type="password" id="password" name="password" class="form-control" ng-model="credentials.password" placeholder="{{ 'PASSWORD_LABEL' | translate }}" ng-minlength="4">
|
||||
</div>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-signup">{{ 'SIGNUP_BTN' | translate }}</button>
|
||||
<button type="submit" class="btn btn-signup btn-rounded btn-block">{{ 'SIGNUP_BTN' | translate }}</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
|
|
@ -1,22 +1,27 @@
|
|||
<section class="auth row" data-ng-controller="PasswordController">
|
||||
<h3 class="col-md-12 text-center">{{ 'PASSWORD_RESTORE_HEADER' | translate }}</h3>
|
||||
<p class="small text-center">{{ 'ENTER_YOUR_EMAIL' | translate }}</p>
|
||||
<div class="col-xs-offset-2 col-xs-8 col-md-offset-3 col-md-6">
|
||||
<form data-ng-submit="askForPasswordReset()" class="signin form-horizontal" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<input type="text" id="username" name="username" class="form-control" data-ng-model="credentials.username" placeholder="{{ 'USERNAME_LABEL' | translate }}">
|
||||
</div>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-primary">{{ 'UPDATE_PASSWORD_LABEL' | translate }}</button>
|
||||
</div>
|
||||
<div data-ng-show="error" class="text-center text-danger">
|
||||
<strong>{{error}}</strong>
|
||||
</div>
|
||||
<div data-ng-show="success" class="text-center text-success">
|
||||
<strong>{{success}}</strong>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<section class="auth valign-wrapper" data-ng-controller="PasswordController">
|
||||
<div class="row valign">
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="col-md-12 text-center" style="padding-bottom: 50px;">
|
||||
<img src="/static/modules/core/img/logo_white.svg" height="100px">
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<form data-ng-submit="askForPasswordReset()" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<input type="text" id="username" name="username" class="form-control" data-ng-model="credentials.username" placeholder="{{ 'USERNAME_OR_EMAIL_LABEL' | translate }}">
|
||||
</div>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-signup btn-rounded btn-block">{{ 'PASSWORD_RESTORE_HEADER' | translate }}</button>
|
||||
</div>
|
||||
<div data-ng-show="error" class="text-center text-danger">
|
||||
<strong>{{error}}</strong>
|
||||
</div>
|
||||
<div data-ng-show="success" class="text-center text-success">
|
||||
<strong>{{success}}</strong>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<header data-ng-include="'/static/modules/core/views/header.client.view.html'"></header>
|
||||
|
||||
<section class="row" data-ng-controller="SettingsController">
|
||||
<h3 class="col-xs-offset-1 col-xs-10 text-center">Edit your profile</h3>
|
||||
<h2 class="col-xs-offset-1 col-xs-10 text-center">Edit your profile</h2>
|
||||
<div class="col-xs-offset-3 col-xs-6">
|
||||
<form name="userForm" data-ng-submit="updateUserProfile(userForm.$valid)" class="signin form-horizontal" autocomplete="off">
|
||||
<fieldset>
|
||||
|
@ -15,18 +15,18 @@
|
|||
|
||||
<div class="form-group row">
|
||||
<div class="col-xs-7 field-title">
|
||||
<b>{{ 'FIRST_NAME_LABEL' | translate }}</b>
|
||||
{{ 'FIRST_NAME_LABEL' | translate }}
|
||||
</div>
|
||||
<div class="col-xs-12 field-input">
|
||||
<input type="text" id="firstName" name="firstName" class="form-control" data-ng-model="user.firstName" placeholder="First Name" ng-pattern="/^[a-zA-Z0-9 \-.]*$/">
|
||||
<input type="text" id="firstName" name="firstName" class="form-control" data-ng-model="user.firstName" ng-pattern="/^[\w0-9 \-.]*$/">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xs-7 field-title">
|
||||
<b>Last Name</b>
|
||||
{{ 'LAST_NAME_LABEL' | translate }}
|
||||
</div>
|
||||
<div class="col-xs-12 field-input">
|
||||
<input type="text" id="lastName" name="lastName" class="form-control" data-ng-model="user.lastName" placeholder="Last Name" ng-pattern="/^[a-zA-Z0-9 \-.]*$/">
|
||||
<input type="text" id="lastName" name="lastName" class="form-control" data-ng-model="user.lastName" ng-pattern="/^[\w0-9 \-.]*$/">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -34,7 +34,7 @@
|
|||
</div>
|
||||
<div class="row form-group">
|
||||
<div class="col-xs-7 field-title">
|
||||
<b>{{ 'LANGUAGE_LABEL' | translate }}</b>
|
||||
{{ 'LANGUAGE_LABEL' | translate }}
|
||||
</div>
|
||||
<div class="col-xs-12 field-input">
|
||||
<select ng-model="user.language" required>
|
||||
|
@ -49,25 +49,28 @@
|
|||
|
||||
<div class="row form-group">
|
||||
<div class="col-xs-7 field-title">
|
||||
<b>Username</b>
|
||||
{{ 'USERNAME_LABEL' | translate }}
|
||||
</div>
|
||||
<div class="col-xs-12 field-input">
|
||||
<input type="text" id="username" name="username" class="form-control" data-ng-model="user.username" placeholder="Username">
|
||||
<input type="text" id="username" name="username" class="form-control" data-ng-model="user.username">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row form-group">
|
||||
<div class="col-xs-7 field-title">
|
||||
<b>{{ 'EMAIL_LABEL' | translate }}</b>
|
||||
{{ 'EMAIL_LABEL' | translate }}
|
||||
</div>
|
||||
<div class="col-xs-12 field-input">
|
||||
<input type="email" id="email" name="email" class="form-control" data-ng-model="user.email" placeholder="Email">
|
||||
<input type="email" id="email" name="email" class="form-control" data-ng-model="user.email">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-large btn-primary" style="font-size:1.6em;">
|
||||
{{ 'SUBMIT_BTN' | translate }}
|
||||
<button type="submit" class="btn btn-signup btn-rounded">
|
||||
{{ 'SAVE_CHANGES' | translate }}
|
||||
</button>
|
||||
<button type="none" ng-click="cancel()" class="btn btn-rounded">
|
||||
{{ 'CANCEL' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,36 +1,45 @@
|
|||
<section class="auth" data-ng-controller="VerifyController">
|
||||
<section ng-if="!isResetSent">
|
||||
<h3 class="col-md-12 text-center">Resend your account verification email</h3>
|
||||
<p class="small text-center">Enter your account email.</p>
|
||||
<div data-ng-show="error" class="text-center text-danger">
|
||||
<strong>{{error}}</strong>
|
||||
</div>
|
||||
<div class="col-xs-offset-2 col-xs-8 col-md-offset-3 col-md-6">
|
||||
<form data-ng-submit="resendVerifyEmail()" class="signin form-horizontal" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<input type="text" id="email" name="email" class="form-control" data-ng-model="credentials.email" placeholder="bob@example.com">
|
||||
</div>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-primary" ng-click="resendVerifyEmail()">{{ 'SUBMIT_BTN' | translate }}</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<section class="auth valign-wrapper" data-ng-controller="VerifyController">
|
||||
|
||||
<section class="row valign" ng-if="!isResetSent">
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="col-md-12 text-center" style="padding-bottom: 50px;">
|
||||
<img src="/static/modules/core/img/logo_white.svg" height="100px">
|
||||
</div>
|
||||
<div data-ng-show="error" class="text-center text-danger">
|
||||
<strong>{{error}}</strong>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<form data-ng-submit="resendVerifyEmail()" class="signin form-horizontal" autocomplete="off">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<input type="text" id="email" name="email" class="form-control" data-ng-model="credentials.email" placeholder="{{ 'ENTER_ACCOUNT_EMAIL' | translate}}">
|
||||
</div>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-signup btn-rounded btn-block" ng-click="resendVerifyEmail()">{{ 'RESEND_VERIFICATION_EMAIL' | translate }}</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section ng-if="isResetSent">
|
||||
<h3 class="col-xs-offset-2 col-xs-8 col-md-offset-3 col-md-6 text-center">Verification Email has been Sent </h3>
|
||||
<div class="col-xs-offset-2 col-xs-8 col-md-offset-3 col-md-6">
|
||||
<h2>
|
||||
{{ 'VERIFICATION_EMAIL_SENT' | translate }} {{username}}.
|
||||
<br> {{ 'NOT_ACTIVATED_YET' | translate }}
|
||||
</h2>
|
||||
<p> {{ 'CHECK_YOUR_EMAIL' | translate }} <a href="mail:polydaic@gmail.com">polydaic@gmail.com</a></p>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-large btn-primary">
|
||||
<section class="row valign" ng-if="isResetSent">
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="col-md-12 text-center" style="padding-bottom: 50px;">
|
||||
<img src="/static/modules/core/img/logo_white.svg" height="100px">
|
||||
</div>
|
||||
<h3 class="col-md-12 text-center">Verification Email has been Sent</h3>
|
||||
<div class="col-md-12">
|
||||
<h2>
|
||||
{{ 'VERIFICATION_EMAIL_SENT' | translate }} {{username}}.
|
||||
<br> {{ 'NOT_ACTIVATED_YET' | translate }}
|
||||
</h2>
|
||||
<p> {{ 'CHECK_YOUR_EMAIL' | translate }} <a href="mail:polydaic@gmail.com">polydaic@gmail.com</a></p>
|
||||
<div class="text-center form-group">
|
||||
<button type="submit" class="btn btn-large btn-primary btn-rounded">
|
||||
<a href="/#!/" style="color:white;">Continue</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -1,14 +1,38 @@
|
|||
<section class="auth" data-ng-controller="VerifyController" ng-init="validateVerifyToken()">
|
||||
|
||||
<section class="row text-center" ng-if="isResetSent">
|
||||
<h3 class="col-md-12"></h3>
|
||||
<a href="/#!/signin" class="col-md-12">{{ 'CONTINUE_TO_LOGIN' | translate }}</a>
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="col-md-12 text-center" style="padding-bottom: 50px;">
|
||||
<img src="/static/modules/core/img/logo_white.svg" height="100px">
|
||||
</div>
|
||||
<h3 class="col-md-12">
|
||||
{{ 'VERIFY_SUCCESS' | translate }}
|
||||
</h3>
|
||||
<div class="col-md-12">
|
||||
<a href="/#!/signin" class="btn btn-signup btn-rounded btn-block">{{ 'CONTINUE_TO_LOGIN' | translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="row text-center" ng-if="!isResetSent">
|
||||
<h3 class="col-md-12">{{ 'VERIFY_ERROR' | translate }}</h3>
|
||||
<a href="/#!/verify" class="col-md-6">{{ 'REVERIFY_ACCOUNT_LINK' | translate }}</a>
|
||||
<a href="/#!/signin" class="col-md-6">{{ 'SIGNIN_BTN' | translate }}</a>
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<div class="col-md-12 text-center" style="padding-bottom: 50px;">
|
||||
<img src="/static/modules/core/img/logo_white.svg" height="100px">
|
||||
</div>
|
||||
<h3 class="col-md-12">
|
||||
{{ 'VERIFY_ERROR' | translate }}
|
||||
</h3>
|
||||
<div class="col-md-12">
|
||||
<a href="/#!/verify" class="btn btn-rounded btn-default">
|
||||
{{ 'REVERIFY_ACCOUNT_LINK' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<a href="/#!/signin" class="btn btn-rounded btn-primary">
|
||||
{{ 'SIGNIN_BTN' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
|
882
public/swagger.json
Normal file
882
public/swagger.json
Normal file
|
@ -0,0 +1,882 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"version": "1.0.0",
|
||||
"title": "TellForm API",
|
||||
"contact": {
|
||||
"name": "TellForm Team",
|
||||
"email": "team@tellform.com"
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Find out how to host your own TellForm instance.",
|
||||
"url": "https://github.com/whitef0x0/tellform"
|
||||
},
|
||||
"host": "api.tellform.com",
|
||||
"basePath": "/",
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"name": "form",
|
||||
"description": "Everything about your Forms"
|
||||
},
|
||||
{
|
||||
"name": "user",
|
||||
"description": "Everything about your Account"
|
||||
}
|
||||
],
|
||||
"securityDefinitions": {
|
||||
"api_key": {
|
||||
"type": "apiKey",
|
||||
"name": "apikey",
|
||||
"in": "header"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"/forms": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"form"
|
||||
],
|
||||
"summary": "Find all forms",
|
||||
"responses": {
|
||||
"405": {
|
||||
"description": "Missing Form Input"
|
||||
},
|
||||
"400": {
|
||||
"description": "Form is Invalid"
|
||||
},
|
||||
"404": {
|
||||
"description": "Form not Found"
|
||||
},
|
||||
"200": {
|
||||
"description": "forms response",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Form"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/form/:form_id": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"form"
|
||||
],
|
||||
"summary": "Find form by ID",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "forms response",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Form"
|
||||
},
|
||||
"headers": {
|
||||
"x-expires": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"form"
|
||||
],
|
||||
"summary": "Create a new form",
|
||||
"description": "Create and save a new form",
|
||||
"operationId": "addForm",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "body",
|
||||
"description": "Form object that is to be created",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Form"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"405": {
|
||||
"description": "Missing Form Input"
|
||||
},
|
||||
"400": {
|
||||
"description": "Form is Invalid"
|
||||
},
|
||||
"404": {
|
||||
"description": "Form not Found"
|
||||
},
|
||||
"200": {
|
||||
"description": "successful operation",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Form"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"x-code-samples": [
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"form"
|
||||
],
|
||||
"summary": "Update an existing form",
|
||||
"description": "",
|
||||
"operationId": "updateForm",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "form",
|
||||
"description": "Form object that needs to be updated",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Form"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"405": {
|
||||
"description": "Missing Form Input"
|
||||
},
|
||||
"400": {
|
||||
"description": "Form is Invalid"
|
||||
},
|
||||
"404": {
|
||||
"description": "Form not Found"
|
||||
},
|
||||
"200": {
|
||||
"description": "successful operation",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Form"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"x-code-samples": [
|
||||
]
|
||||
}
|
||||
},
|
||||
"/users/me": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Retrieve current User",
|
||||
"description": "",
|
||||
"operationId": "getUser",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"responses": {
|
||||
"500": {
|
||||
"description": "Could not Update User"
|
||||
},
|
||||
"401": {
|
||||
"description": "User is not Signed in"
|
||||
},
|
||||
"403": {
|
||||
"description": "User is not Authorized"
|
||||
},
|
||||
"404": {
|
||||
"description": "User does not exsit"
|
||||
},
|
||||
"200": {
|
||||
"description": "successful operation",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/User"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/users": {
|
||||
"put": {
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Update the current user",
|
||||
"description": "",
|
||||
"operationId": "updateUser",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "body",
|
||||
"description": "User object that needs to be updated",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/User"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"500": {
|
||||
"description": "Could not Update User"
|
||||
},
|
||||
"401": {
|
||||
"description": "User is not Signed in"
|
||||
},
|
||||
"403": {
|
||||
"description": "User is not Authorized"
|
||||
},
|
||||
"404": {
|
||||
"description": "User does not exsit"
|
||||
},
|
||||
"200": {
|
||||
"description": "successful operation",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/User"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"x-code-samples": [
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
"definitions": {
|
||||
"User": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"language",
|
||||
"email",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"firstName": {
|
||||
"type": "string",
|
||||
"description": "First name of User",
|
||||
"example": "John"
|
||||
},
|
||||
"lastName": {
|
||||
"type": "string",
|
||||
"description": "First name of User",
|
||||
"example": "Doe"
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"en",
|
||||
"fr",
|
||||
"es",
|
||||
"it",
|
||||
"de"
|
||||
],
|
||||
"default": "en",
|
||||
"required": "User must have a language",
|
||||
"description": "Language of User (for internationalization)",
|
||||
"example": "fr"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"required": "User email cannot be blank",
|
||||
"unique": "true",
|
||||
"description": "Email of User",
|
||||
"example": "john.doe@somewhere.com"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"required": "Username cannot be blank",
|
||||
"unique": "true",
|
||||
"description": "Username of User",
|
||||
"example": "johndoe"
|
||||
},
|
||||
"roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"user",
|
||||
"admin",
|
||||
"superuser"
|
||||
]
|
||||
},
|
||||
"default": [ "user" ],
|
||||
"description": "Security Roles of User"
|
||||
}
|
||||
},
|
||||
"lastModified": {
|
||||
"type": "date",
|
||||
"description": "Date that user was last modified",
|
||||
"example": "2016-08-26T20:19:30.146Z"
|
||||
},
|
||||
"created": {
|
||||
"type": "date",
|
||||
"description": "Date that user was created on",
|
||||
"example": "5dHuKJgeCZmFOdJTnmg0lrxApmz0tbbBrM59rTv4k79"
|
||||
},
|
||||
"resetPasswordToken": {
|
||||
"type": "string",
|
||||
"description": "Reset password token of User",
|
||||
"example": "5dHuKJgeCZmFOdJTnmg0lrxApmz0tbbBrM59rTv4k79"
|
||||
},
|
||||
"resetPasswordExpires": {
|
||||
"type": "date",
|
||||
"example": "2016-08-26T20:19:30.146Z",
|
||||
"description": "Date that the User's password reset token expires"
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"description": "Verification token of User",
|
||||
"example": "5dHuKJgeCZmFOdJTnmg0lrxApmz0tbbBrM59rTv4k79"
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "string",
|
||||
"unique": true,
|
||||
"index": true,
|
||||
"sparse": true,
|
||||
"description": "API Key of User",
|
||||
"example": "5dHuKJgeCZmFOdJTnmg0lrxApmz0tbbBrM59rTv4k79"
|
||||
}
|
||||
},
|
||||
"LogicJump": {
|
||||
"expressionString": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"field == static",
|
||||
"field != static",
|
||||
"field > static",
|
||||
"field >= static",
|
||||
"field <= static",
|
||||
"field < static",
|
||||
"field contains static",
|
||||
"field !contains static",
|
||||
"field begins static",
|
||||
"field !begins static",
|
||||
"field ends static",
|
||||
"field !ends static"
|
||||
]
|
||||
},
|
||||
"fieldA": {
|
||||
"$ref": "#/definitions/FormField"
|
||||
},
|
||||
"valueB": {
|
||||
"type": "string"
|
||||
},
|
||||
"jumpTo": {
|
||||
"$ref": "#/definitions/FormField"
|
||||
}
|
||||
},
|
||||
"FieldOption": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"option_id": {
|
||||
"type": "number"
|
||||
},
|
||||
"option_title": {
|
||||
"type": "string"
|
||||
},
|
||||
"option_value": {
|
||||
"type": "string",
|
||||
"trim": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"RatingField": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"steps": {
|
||||
"type": "number",
|
||||
"min": 1,
|
||||
"max": 10
|
||||
},
|
||||
"shape": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Heart",
|
||||
"Star",
|
||||
"thumbs-up",
|
||||
"thumbs-down",
|
||||
"Circle",
|
||||
"Square",
|
||||
"Check Circle",
|
||||
"Smile Outlined",
|
||||
"Hourglass",
|
||||
"bell",
|
||||
"Paper Plane",
|
||||
"Comment",
|
||||
"Trash"
|
||||
]
|
||||
},
|
||||
"validShapes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"FormField": {
|
||||
"required": [
|
||||
"title",
|
||||
"fieldType"
|
||||
],
|
||||
"properties": {
|
||||
"isSubmission": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specifies whether Field is part of a Submission or not",
|
||||
"example": true
|
||||
},
|
||||
"submissionId": {
|
||||
"type": "string",
|
||||
"description": "ID of Submission that this Field belongs to",
|
||||
"example": "57bca0969ca8e18b825bcc2b"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"trim": true,
|
||||
"required": "Field Title cannot be blank",
|
||||
"description": "Description of Field",
|
||||
"example": "Your Current University"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Description of Field",
|
||||
"example": "Please do not use abbreviations for your school name"
|
||||
},
|
||||
"logicJump": {
|
||||
"$ref": "#/definitions/FormField"
|
||||
},
|
||||
"ratingOptions": {
|
||||
"type": "#/definitions/RatingField"
|
||||
},
|
||||
"fieldOptions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "FieldOption"
|
||||
}
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Specifies whether Field is required",
|
||||
"example": true
|
||||
},
|
||||
"disabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specifies whether Field is disabled",
|
||||
"example": true
|
||||
},
|
||||
"deletePreserved": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specifies whether Field should be preserved if it is deleted",
|
||||
"example": false
|
||||
},
|
||||
"validFieldTypes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"fieldType": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"enum": [
|
||||
"textfield",
|
||||
"date",
|
||||
"email",
|
||||
"link",
|
||||
"legal",
|
||||
"url",
|
||||
"textarea",
|
||||
"statement",
|
||||
"welcome",
|
||||
"thankyou",
|
||||
"file",
|
||||
"dropdown",
|
||||
"scale",
|
||||
"rating",
|
||||
"radio",
|
||||
"checkbox",
|
||||
"hidden",
|
||||
"yes_no",
|
||||
"natural",
|
||||
"stripe",
|
||||
"number"
|
||||
],
|
||||
"description": "Type of Field",
|
||||
"example": "textfield"
|
||||
},
|
||||
"fieldValue": {
|
||||
"type": "string",
|
||||
"description": "Value of Field",
|
||||
"example": "University of British Columbia"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VisitorData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"referrer": {
|
||||
"type": "string",
|
||||
"description": "Referring site of Form Visitor",
|
||||
"example": "http://google.com"
|
||||
},
|
||||
"lastActiveField": {
|
||||
"type": "string",
|
||||
"description": "ID of Last Active Field",
|
||||
"example": "57bca0969ca8e18b825bcc2b"
|
||||
},
|
||||
"timeElapsed": {
|
||||
"type": "number",
|
||||
"description": "Time Elasped for Visitor on Form (in seconds)",
|
||||
"example": "333.33"
|
||||
},
|
||||
"isSubmitted": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether user submitted form before leaving page",
|
||||
"example": false
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"description": "Language of User using form",
|
||||
"example": "en"
|
||||
},
|
||||
"ipAddr": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "IP Address of User",
|
||||
"example": "324.332.322.333"
|
||||
},
|
||||
"deviceType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"desktop",
|
||||
"phone",
|
||||
"tablet",
|
||||
"other"
|
||||
],
|
||||
"default": "other",
|
||||
"description": "Device Type of User",
|
||||
"example": "phone"
|
||||
},
|
||||
"userAgent": {
|
||||
"type": "string",
|
||||
"description": "User Agent of User",
|
||||
"example": "Mozilla/5.0 (Linux; <Android Version>; <Build Tag etc.>) AppleWebKit/<WebKit Rev> (KHTML, like Gecko) Chrome/<Chrome Rev> Mobile Safari/<WebKit Rev>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Button": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"format": "url",
|
||||
"description": "URL of Button Link",
|
||||
"example": "http://you-are-awesome.com"
|
||||
},
|
||||
"action": {
|
||||
"type": "string",
|
||||
"description": "Angular Action fired during Button click",
|
||||
"example": "openModal()"
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"description": "Text of Button",
|
||||
"example": "Go to HomePage"
|
||||
},
|
||||
"bgColor": {
|
||||
"type": "string",
|
||||
"pattern": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
|
||||
"default": "#5bc0de",
|
||||
"description": "Background Color of Button (in hex)",
|
||||
"example": "#5bc0de"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"pattern": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
|
||||
"default": "#ffffff",
|
||||
"description": "Font Color of Button (in hex)",
|
||||
"example": "#ffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FormSubmission": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"language",
|
||||
"admin",
|
||||
"title"
|
||||
],
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"required": "Form Title cannot be blank"
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"en",
|
||||
"fr",
|
||||
"es",
|
||||
"it",
|
||||
"de"
|
||||
],
|
||||
"default": "en",
|
||||
"required": "Form must have a language"
|
||||
},
|
||||
"admin": {
|
||||
"$ref": "#/definitions/User",
|
||||
},
|
||||
"ipAddr": {
|
||||
"type": "string"
|
||||
},
|
||||
"geoLocation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Country": {
|
||||
"type": "string"
|
||||
},
|
||||
"Region": {
|
||||
"type": "string"
|
||||
},
|
||||
"City": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pdfFilePath": {
|
||||
"type": "string"
|
||||
},
|
||||
"pdf": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
"timeElapsed": {
|
||||
"type": "number"
|
||||
},
|
||||
"percentageComplete": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Form": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"language",
|
||||
"admin",
|
||||
"title"
|
||||
],
|
||||
"properties": {
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"required": "Form Title cannot be blank",
|
||||
"description": "Public Title of Form",
|
||||
"example": "UBC CPSC221 Course Waitlist Form"
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"en",
|
||||
"fr",
|
||||
"es",
|
||||
"it",
|
||||
"de"
|
||||
],
|
||||
"default": "en",
|
||||
"required": "Form must have a language",
|
||||
"description": "Language of Form",
|
||||
"example": "en"
|
||||
},
|
||||
"analytics": {
|
||||
"type": "object",
|
||||
"description": "Analytics of Form",
|
||||
"properties": {
|
||||
"gaCode": {
|
||||
"type": "string",
|
||||
"description": "Analytics of Form",
|
||||
"example": "UA-000000-01"
|
||||
},
|
||||
"visitors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "#/definitions/VisitorData"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"form_fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "FormField"
|
||||
}
|
||||
},
|
||||
"submissions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/FormSubmission"
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"type": "User",
|
||||
"description": "User that this Form belongs to"
|
||||
},
|
||||
"startPage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showStart": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specifies whether Form StarPage should be displayed",
|
||||
"example": false
|
||||
},
|
||||
"introTitle": {
|
||||
"type": "string",
|
||||
"default": "Welcome to Form",
|
||||
"description": "Title of Form's StartPage",
|
||||
"example": "Welcome to our Awesome Form!"
|
||||
},
|
||||
"introParagraph": {
|
||||
"type": "string",
|
||||
"description": "Introduction paragraph for Form's StartPage.",
|
||||
"example": "Welcome to our Awesome Form!"
|
||||
},
|
||||
"introButtonText": {
|
||||
"type": "string",
|
||||
"default": "Start",
|
||||
"description": "StartPage Continue Button",
|
||||
"example": "Continue"
|
||||
},
|
||||
"buttons": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
type: "Button"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"hideFooter": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specifies whether to hide or show Form Footer",
|
||||
"example": true
|
||||
},
|
||||
"isLive": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Specifies whether form is Publically available or Private",
|
||||
"example": true
|
||||
},
|
||||
"design": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"colors": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backgroundColor": {
|
||||
"type": "string",
|
||||
"pattern": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
|
||||
"default": "#fff",
|
||||
"description": "Background color of Form",
|
||||
"example": "#4c4c4c"
|
||||
},
|
||||
"questionColor": {
|
||||
"type": "string",
|
||||
"match": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
|
||||
"default": "#333",
|
||||
"description": "Question text font color (in hex)",
|
||||
"example": "#fff"
|
||||
},
|
||||
"answerColor": {
|
||||
"type": "string",
|
||||
"match": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
|
||||
"default": "#333",
|
||||
"description": "Answer text font color (in hex)",
|
||||
"example": "#f9f9f9"
|
||||
},
|
||||
"buttonColor": {
|
||||
"type": "string",
|
||||
"match": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
|
||||
"default": "#fff",
|
||||
"description": "Background color of Form Buttons (in hex)",
|
||||
"example": "#555"
|
||||
},
|
||||
"buttonTextColor": {
|
||||
"type": "string",
|
||||
"pattern": "/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/",
|
||||
"default": "#333",
|
||||
"description": "Font color of Form Buttons (in hex)",
|
||||
"example": "#fff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"font": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,8 +13,6 @@ var init = require('../config/init')(),
|
|||
fs = require('fs-extra'),
|
||||
chalk = require('chalk');
|
||||
|
||||
console.log(config);
|
||||
|
||||
// Bootstrap db connection
|
||||
var db = mongoose.connect(config.db.uri, config.db.options, function(err) {
|
||||
if (err) {
|
||||
|
|
Loading…
Reference in a new issue