diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index d28dc25a..ccb1e011 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "fh-js-sdk", - "version": "2.17.4", + "version": "2.17.5", "dependencies": { "loglevel": { "version": "0.6.0", diff --git a/package.json b/package.json index a59276ef..aa105067 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fh-js-sdk", - "version": "2.17.4", + "version": "2.17.5", "description": "feedhenry js sdk", "main": "dist/feedhenry.js", "browser": { diff --git a/src/appforms/src/core/040-Model06Submission.js b/src/appforms/src/core/040-Model06Submission.js index cdbac38f..9e760ed8 100644 --- a/src/appforms/src/core/040-Model06Submission.js +++ b/src/appforms/src/core/040-Model06Submission.js @@ -207,51 +207,123 @@ appForm.models = function(module) { }); }; + /** + * Function for removing any values from a submisson that in hidden fields. + * + * @param {function} cb -- Callback function + */ + Submission.prototype.removeHiddenFieldValues = function(cb) { + var self = this; + async.waterfall([ + function checkSubmissionRules(callback) { + self.checkRules(callback); + }, + function getForm(ruleState, callback) { + self.getForm(function(err, formModel) { + return callback(err, ruleState, formModel); + }); + }, + function pruneHiddenFields(ruleState, formModel, callback) { + //Getting hidden pages and fields. + + var actions = ruleState.actions; + + var ruleTypes = ["fields", "pages"]; + + //For page and field rule actions, find the hidden fields. + var allHiddenFieldIds = _.map(ruleTypes, function(ruleType) { + var fieldIds = []; + + var hidden = _.map(actions[ruleType] || {}, function(ruleAction, fieldOrPageId) { + if (ruleAction.action === 'hide') { + return fieldOrPageId; + } else { + return null; + } + }); + + //If it is a hidden page, need to check for all fields that are in the page. + //All of these fields are considered hidden. + if(ruleType === 'pages') { + fieldIds = _.map(hidden, function(pageId) { + var pageModel = formModel.getPageModelById(pageId) || {}; + + return pageModel.fieldsIds; + }); + } else { + fieldIds = hidden; + } + + return _.compact(_.flatten(fieldIds)); + }); + + allHiddenFieldIds = _.flatten(allHiddenFieldIds); + + //Now remove any values from from the submission containing hidden fields + async.forEachSeries(allHiddenFieldIds, function(fieldId, cb) { + self.removeFieldValue(fieldId, null, cb); + }, function(err){ + if(err) { + $fh.forms.log.e("Error removing fields", err); + } + + return callback(err); + }); + + } + ], cb); + }; + /** * submit current submission to remote * @param {Function} cb [description] * @return {[type]} [description] */ Submission.prototype.submit = function(cb) { - var that = this; + var self = this; $fh.forms.log.d("Submission submit: "); var targetStatus = 'pending'; - var validateResult = true; - this.set('timezoneOffset', appForm.utils.getTime(true)); - this.pruneNullValues(); - this.pruneRemovedFields(function(err) { - if (err) { - $fh.forms.log.e("Submission submit validateForm: Error removing deleted fields from submission ", err); - cb(err); - } else { - that.performValidation(function(err, res) { + self.set('timezoneOffset', appForm.utils.getTime(true)); + self.pruneNullValues(); + + async.waterfall([ + function(callback) { + self.removeHiddenFieldValues(callback); + }, + function(callback) { + self.pruneRemovedFields(callback); + }, + function(callback) { + self.performValidation(function(err, validationResult) { if (err) { $fh.forms.log.e("Submission submit validateForm: Error validating form ", err); - cb(err); - } else { - $fh.forms.log.d("Submission submit: validateForm. Completed result", res); - var validation = res.validation; - if (validation.valid) { - $fh.forms.log.d("Submission submit: validateForm. Completed Form Valid", res); - that.set('submitDate', new Date()); - that.changeStatus(targetStatus, function(error) { - if (error) { - cb(error); - } else { - that.emit('submit'); - cb(null, null); - } - }); - } else { - $fh.forms.log.d("Submission submit: validateForm. Completed Validation error", res); - that.emit('validationerror', validation); - cb('Validation error'); - } } + + return callback(err, validationResult); }); + }, + function(validationResult, callback) { + $fh.forms.log.d("Submission submit: validateForm. Completed result", validationResult); + var validation = validationResult.validation || {}; + if (validation.valid) { + $fh.forms.log.d("Submission submit: validateForm. Completed Form Valid", validationResult); + self.set('submitDate', new Date()); + self.changeStatus(targetStatus, function(error) { + if (error) { + callback(error); + } else { + self.emit('submit'); + callback(null, null); + } + }); + } else { + $fh.forms.log.d("Submission submit: validateForm. Completed Validation error", validationResult); + self.emit('validationerror', validation); + callback('Validation error'); + } } - }); + ], cb); }; Submission.prototype.getUploadTask = function(cb) { var taskId = this.getUploadTaskId(); @@ -854,7 +926,7 @@ appForm.models = function(module) { targetArr = this.getInputValueObjectById(fieldId).fieldValues; } //If no index is supplied, all values are removed. - if (typeof index === 'undefined') { + if (index === null || typeof index === 'undefined') { valsRemoved = targetArr.splice(0, targetArr.length); } else { if (targetArr.length > index) { diff --git a/src/appforms/tests/tests/core/040-Model06Submission.js b/src/appforms/tests/tests/core/040-Model06Submission.js index 387efe30..960c7116 100644 --- a/src/appforms/tests/tests/core/040-Model06Submission.js +++ b/src/appforms/tests/tests/core/040-Model06Submission.js @@ -741,4 +741,280 @@ describe("Submission model", function() { }); }); }); + + describe("Submissions values should be removed from hidden fields", function() { + + var testHidingFieldsForm = { + "_id":"582dbc9009ce8d9c6e63f532", + "name":"hiddenvalue", + "pageRules":[ + { + "type":"skip", + "_id":"582dbe8209ce8d9c6e63f538", + "targetPage":[ + "582dbce709ce8d9c6e63f535" + ], + "ruleConditionalStatements":[ + { + "sourceField":"582dbce709ce8d9c6e63f533", + "restriction":"is", + "sourceValue":"hidepage", + "_id":"582dbe8209ce8d9c6e63f539" + } + ], + "ruleConditionalOperator":"and", + "relationType":"and" + } + ], + "fieldRules":[ + { + "type":"hide", + "_id":"582dbda67ae62c9b6e4afedc", + "targetField":[ + "582dbce709ce8d9c6e63f534" + ], + "ruleConditionalStatements":[ + { + "sourceField":"582dbce709ce8d9c6e63f533", + "restriction":"is", + "sourceValue":"hidenumber", + "_id":"582dbda67ae62c9b6e4afedd" + } + ], + "ruleConditionalOperator":"and", + "relationType":"and" + }, + { + "type":"hide", + "_id":"582dbda67ae62c9b6e4afede", + "targetField":[ + "582dbce709ce8d9c6e63f537" + ], + "ruleConditionalStatements":[ + { + "sourceField":"582dbce709ce8d9c6e63f533", + "restriction":"is", + "sourceValue":"hidefile", + "_id":"582dbda67ae62c9b6e4afedf" + } + ], + "ruleConditionalOperator":"and", + "relationType":"and" + } + ], + "pages":[ + { + "_id":"582dbc9009ce8d9c6e63f531", + "name":"Page 1", + "fields":[ + { + "required":true, + "type":"text", + "name":"Text", + "_id":"582dbce709ce8d9c6e63f533", + "adminOnly":false, + "fieldOptions":{ + "validation":{ + "validateImmediately":true + } + }, + "repeating":false, + "dataSourceType":"static" + }, + { + "required":true, + "type":"number", + "name":"Number", + "_id":"582dbce709ce8d9c6e63f534", + "adminOnly":false, + "fieldOptions":{ + "validation":{ + "validateImmediately":true + } + }, + "repeating":false, + "dataSourceType":"static" + } + ] + }, + { + "name":"Page 2", + "_id":"582dbce709ce8d9c6e63f535", + "fields":[ + { + "required":true, + "type":"text", + "name":"Text 2", + "_id":"582dbce709ce8d9c6e63f536", + "adminOnly":false, + "fieldOptions":{ + "validation":{ + "validateImmediately":true + } + }, + "repeating":false, + "dataSourceType":"static" + }, + { + "required":true, + "type":"file", + "name":"File", + "_id":"582dbce709ce8d9c6e63f537", + "adminOnly":false, + "fieldOptions":{ + "validation":{ + "validateImmediately":true + } + }, + "repeating":false, + "dataSourceType":"static" + } + ] + } + ], + "lastUpdated":"2016-11-17T14:28:18.397Z", + "dateCreated":"2016-11-17T14:20:00.607Z", + "lastDataRefresh":"2016-11-17T14:28:18.397Z", + "pageRef":{ + "582dbc9009ce8d9c6e63f531":0, + "582dbce709ce8d9c6e63f535":1 + }, + "fieldRef":{ + "582dbce709ce8d9c6e63f533":{ + "page":0, + "field":0 + }, + "582dbce709ce8d9c6e63f534":{ + "page":0, + "field":1 + }, + "582dbce709ce8d9c6e63f536":{ + "page":1, + "field":0 + }, + "582dbce709ce8d9c6e63f537":{ + "page":1, + "field":1 + } + }, + "lastUpdatedTimestamp":1479392898397 + }; + + function getFormSubmission(cb) { + new appForm.models.Form({ + formId: "582dbc9009ce8d9c6e63f532", + rawMode: true, + rawData: testHidingFieldsForm + }, function(err, form) { + assert.ok(!err, "Expected no error getting a form", err); + + cb(err, form.newSubmission()); + }); + } + + function addValue(fieldId, value, submission, cb) { + + //Adding a value for the number field. + submission.addInputValue({ + fieldId: fieldId, + value: value + }, function(err) { + assert.ok(!err, "Expected no error when adding a value", err); + + return cb(err, submission); + }); + } + + it("Hiding a field should remove a field value", function(done) { + + async.waterfall([ + async.apply(getFormSubmission), + //Adding a number value to the number field. This should be removed after hiding. + async.apply(addValue, "582dbce709ce8d9c6e63f534", 22), + //Using the hidenumber value to hide the number field on page 2 + async.apply(addValue, "582dbce709ce8d9c6e63f533", "hidenumber"), + function (submission, cb) { + //The number value should still be there - users may unhide the field at any point when editing the form. + assert.equal(22, submission.getFormFields()[0].fieldValues[0]); + cb(null, submission); + }, + function submitForm(submission, cb) { + submission.submit(function(err) { + assert.ok(err, "Expected an error as the submission is not complete"); + + //The hidden number field should have been removed + var numberFieldValues = _.findWhere(submission.getFormFields(), {fieldId: "582dbce709ce8d9c6e63f534"}); + + assert.equal(0, numberFieldValues.fieldValues.length); + + //The text field should be there. + var textFieldValues = _.findWhere(submission.getFormFields(), {fieldId: "582dbce709ce8d9c6e63f533"}); + + assert.equal("hidenumber", textFieldValues.fieldValues[0]); + cb(); + }); + } + ], done); + }); + + it("Hiding a Page should remove values in all fields in that page", function(done) { + var textValue = "sometextvalue"; + var fileName = "testhiddenfieldfile.txt"; + var fileSystem = appForm.utils.fileSystem; + + async.waterfall([ + function createTextFile(cb){ + fileSystem.save(fileName, "This file is going to be removed", function(err) { + assert.ok(!err, "Expected no error " + err); + cb(err); + }); + }, + async.apply(getFormSubmission), + async.apply(addValue, "582dbce709ce8d9c6e63f536", textValue), + function readTextFile(submission, cb){ + fileSystem.readAsFile(fileName, function(err, file){ + assert.ok(!err, "Expected no error " + err); + assert.ok(file instanceof File, "Expected a file instance."); + cb(err, submission, file); + }); + }, + function(submission, file, cb) { + addValue("582dbce709ce8d9c6e63f537", file, submission, cb); + }, + function (submission, cb) { + //The number value should still be there - users may unhide the field at any point when editing the form. + var formFields = submission.getFormFields(); + assert.equal(textValue, formFields[0].fieldValues[0]); + + //Expecting a file value to be saved + assert.ok(formFields[1].fieldValues[0].hashName, "Expected A File Hash Name"); + + cb(null, submission); + }, + //Applying a value to the text field to hide the page + async.apply(addValue, "582dbce709ce8d9c6e63f533", "hidepage"), + function submitForm(submission, cb) { + submission.submit(function(err) { + assert.ok(err, "Expected an error as the submission is not complete"); + + var formFields = submission.getFormFields(); + //The hidden text field in the hidden page should be empty. + var textFieldValues = _.findWhere(formFields, {fieldId: "582dbce709ce8d9c6e63f536"}); + assert.equal(0, textFieldValues.fieldValues.length); + + //The hidden file field values should be removed. + var fileFieldValues = _.findWhere(formFields, {fieldId: "582dbce709ce8d9c6e63f537"}); + assert.equal(0, fileFieldValues.fieldValues.length); + + //The text field on page 1 should be there. + var originalTextField = _.findWhere(submission.getFormFields(), {fieldId: "582dbce709ce8d9c6e63f533"}); + assert.equal("hidepage", originalTextField.fieldValues[0]); + + cb(); + }); + } + ], done); + }); + + }); });