Sending a form through PHP, autosaving with JS every ten minutes - javascript

The Environment
PHP: Symfony -> Twig, Bootstrap
Doctrine -> Mongodb
Jquery
The Project
The timer is working, counting the interval, and I can send the form. This is good, but once I get to the part where the form is sent I seem to be running into two problems:
interval counter working
form submitting automatically, then
The null fields do not get accepted
The form does not get validated
The form itself is setup through a custom built PHP graphical interface that allows you to set the simple things very easily: fields, fieldtypes, names and labels, etc.
This is cool, but it means that you can't go in and tinker very much with the form or at all really. The most you can change is aesthetics in the template. Although you could wrap each form element in another form element or some kind of html identifier, this is an option, but a tedious one...
On a valid check the form gets upserted and all is well the else catches the non-valid form and lets the user know that some fields are not filled in. I would post that code here, but I think it would be frowned upon. Hopefully you understand the PHP concept.
The part that I can share is the JS:
autoSaveForm = function() {
var form = $('form'),
inputs = form.find('input');
form.on('submit', function() {
inputs.each( function( input ) {
if (input.attr("disabled") === true){
input.removeAttr("disabled");
}
})
});
function counter(){
var present = $('.minutes').attr('id'),
past = present;
$('.minutes').text(past);
setInterval(function(){
past--;
if(past>=0){
$('.minutes').text(past);
}
if(past==0){
$('.minutes').text(present);
}
},1000);
}
function disableInputs(){
inputs.each( function( input ) {
if (input.val() === ""){
input.attr("disabled", true);
}
});
}
// Start
counter();
// Loop
setInterval(function(){
counter();
form.submit()
},10000);
};
This sets a counter on the page and counts through an interval, it's set to seconds right now for testing, but eventually will be 10 minutes.
Possible Solutions
disable the null fields before submit and remove the attribute after submit is accomplished
this needs to be done so that the user can do a final completed save at the end of the process
this doesn't actaully seem to be disabling the fields
Their were a few things I found about adding in novalidate to the html, but it wasn't supported in all browsers and it might get tricky with the static nature of the forms.
Post the form with ajax and append a save true variable to the end of the url, then redirect it in PHP Controller with a check on the URI var although:
then I still have the null fields
to solve this: set a random value or default value to the fields as they are being passed through. haven't tried this yet
Something kind of like what this question is addressing, although I have some questions still about the null fields - Autosaving Form with Jquery and PHP
Sending the form through ajax
Any input would be greatly appreciated. :) Thanks

Related

Clients using `GET` requests for a form, even though `POST` is defined. is javascript iframe the cause?

I have two subsequent forms on my website with POST method.
The first page of my website first.php contains this code:
<form action="a.php" method="POST" target="_blank">
<input name="value" type="hidden" value="foo"/>
<div class="button"><label><span class="icon"></span>
<input type="submit" class="button-graphic ajax" value="Click Here"></label></div></form>
a.php can be accessed only via this POST request (otherwise user will get method not allowed 405 error)
Once submitted, this form opens a.php with an AJAX modal window.
a.php contains another form:
<form action="b.php" method="POST" target="_blank">
<input name="bar" type="hidden" value="none"/>
<div class="border"><label><input type="submit" class="button-graphic2 tracking" value="Continue"></label></div></form>
When a user clicks Submit in the second form, it will open b.php,
which can also be accessed only via POST request (otherwise - 405 error).
The only difference I can think about between these forms is that the second one contains a tracking js class (opening an iframe). this is the js code:
$(document).ready(function() {
$(".tracking").click(function(){
var iframe = document.createElement('iframe');
iframe.style.width = '0px';
iframe.style.height = '0px';
iframe.style.display = 'block';
document.body.appendChild(iframe);
iframe.src = '/track.htm';
});
This is done in order to track a conversion using a third party script which is being execuated from track.htm
I noticed that I am having a problem with about 5% of my iPad visitors.
they open a.php properly with a POST request, but when they go ahead to continue and open b.php as well, about 5% sends out a GET request instead of the desired POST request, causing them to get an 405 error and leave the website.
I know that these are real human users as I can see some of them trying several times to open b.php and keep getting these 405 errors.
Could this be caused because simultaneously their device is using a GET request to obtain track.htm? and this is some glitch?
How can this be solved?
EDIT 4.4.2015:
Since there's a chance that firing the tracking script is causing this, I would like to know if there's another fire to fire it (or track that adwords conversion), without causing these iPad user to use "GET" requests for the form as well.
EDIT 10.4.2015:
This is the jquery code of the ajax class, that effects both first.php and perhaps a.php, as first.php is the parent frame:
$(document).ready(function() {
$(".ajax").click(function(t) {
t.preventDefault();
var e = $(this).closest("form");
return $.colorbox({
href: e.attr("action"),
transition: "elastic",
overlayClose: !1,
maxWidth: $("html").hasClass("ie7") ? "45%" : "false",
opacity: .7,
data: {
value: e.find('input[name="value"]').val(),
}
}), !1
})
}),
Technically, it shouldn't happen. The iframe created by your tracking script pointed to /track.htm, so there shouldn't be any GET request to your b.php page.
On the other hand, just thinking out loud here, there're a few scenario that could happen because of "real world" user.
The users happen to have bookmark the b.php page, thus causing them to open it using GET when they try to re-open the page using their bookmark.
The users tried to refresh the page b.php, then get warned about "Form re-submission". Being clueless as most real user are, they canceled the form re-submission, then click on the address bar and click GO on their browser with the sole intention of reloading the page. This could also cause the GET request to send to the b.php page.
Considering the best practice when designing the page flow for form submission, it might be better for you to only "process" your form data in b.php and then return a 302 Redirect to another page that show the result using a GET request. This will allow users to "refresh" the page without double submitting the form, and also allow user to bookmark the result page too.
This doesn't answer your question but as it entails to the GET glitch but as things stand, ~5% of your iPad visitors can't sign up because the code only accepts POST and so far no one can figure this out. So I propose a change of strategy, at least in the mean time.
Preventing CSRF by only accepting POST requests is already known to not work. Your choice of accepting only this request method as a means of security is what ultimately results in the 405. There are better ways.
One example of is using a CSRF token, specifically the Synchronizer Token Pattern.
The idea behind a CSRF token is that when you generate the form, you also generate a "key" which you tie to the form. When that form is submitted, if it doesn't have the key or the key isn't the right one, you don't bother processing the form. The Syncronizer Token Pattern gets fancy in that it changes the expect key each time (in the form field implementation, giving the <input type="hidden"> field a new name attribute each time) in addition to the value.
Have your code in a.php generate a random token and
store it as a session variable on the server. Output the token in the form as a hidden field.
Before processing the request in b.php, ensure the token value is in the request data and ensure it has the expected value.
You can first check for $_POST data and if it is missing, check for $_GET data. Regardless of which array contains the data, if the data does not have a valid CSRF token, respond with a 4xx error.
If the token is good, consume the token and process the request.
If the token is missing or is invalid, return a 4xx response code.
Another way would be to set your field names to random values each time the form is generated. So instead of <input name="value" type="hidden" value="foo"/> or <input name="bar" type="hidden" value="none"/>.
// ... in an importable file somewhere ...
// Generate our tokens
function token($len = 13) {
$chrs = 'abcdefghijklmnopqrstuvwxyz0123456789_';
$str = '';
$upper_lim = strlen($chrs) - 1;
for ($i = 0; $i < $len; $i++) {
$idx = rand(0, $upper_lim);
$str .= rand(0, 1) ? strtoupper($chrs[$idx]) : $chrs[$idx];
}
return $str;
}
function magic_set_function($key, $value) {
$_SESSION[$key] = $value;
}
function magic_get_function($key) {
return (array_key_exists($key, $_SESSION) ? $_SESSION[$key] : NULL)
}
function validate_request() {
$data = !empty($_POST) ? $_POST : $_GET;
if ( empty($data) ) { return false; }
// Ensure the tokens exist (hopefully not too costly)
$field_tokens = magic_get_function('field_tokens');
if ( $field_tokens) === NULL ) { return false; }
$csrf_token_name = $field_tokens['token'];
$given_csrf_token = $data[$csrf_token_name];
// Get our CSRF token
$expected_csrf_token = magic_get_function('csrf_token');
// ensure we're expecting a request / that we have generated a CSRF
if ( $expected_csrf_token === NULL ||
$expected_csrf_token !== $given_csrf_token) {
return FALSE;
}
// After whatever other checks you want...
return TRUE;
}
function fetch_data() {
$data = empty($_POST) == FALSE ? $_POST : $_GET;
if (empty($data ) { throw new DataLoadException(); }
// Ensure the tokens exist (hopefully not too costly)
$field_tokens = magic_get_function('field_tokens');
if ( $field_tokens) === NULL ) { throw new TokenLoadException(); }
foreach ($field_tokens as $field_name => $token_name) {
if ( isset($data[$token_name]) ) {
$data[$field_name] = $data[$token_name];
unset($data[$token_name]);
}
}
return $data;
}
// first.php/a.php/b.php (wherever necessary)
// ...
$tokens = array();
// our csrf token
$csrf_token = token();
$field_names = array('value', 'bar', 'token');
$field_values = array('value'=>'foo', 'bar' => 'none', 'token' => $csrf_token);
// Tokenize errthing...
foreach ($field_names as $k => $field_name) {
// and generate random strings
$tokens[$field_name] = token();
}
// You NEED TO STORE THESE TOKENS otherwise submissions lose context
magic_set_function('field_tokens', $tokens);
magic_set_function('csrf_token', $csrf_token); // dup, but j.i.c.
// first.php
printf('<input type="hidden" name="%s" value="%s"/>', $tokens['value'], $field_values['value']);
// ...
// a.php
// Get the data... (POST/GET)
if (ensure_valid_request() !== TRUE) { handle_invalid_request(); }
$data = fetch_data();
// ...
// Tokenize errthing, generate a csrf, store the values, etc.
// ...
printf('<input type="hidden" name="%s" value="%s"/>', $tokens['bar'], $field_values['bar']);
// ...
// b.php
// ... You get the idea ...
It doesn't answer your question of why 5% are sending GET Requests but it does solve your overall problem on both a security and user level.
EDIT:
To specifically answer OPs questions in comments:
"(1) does this require using cookies? (a session means cookies right?)"
Read up on PHP Sessions and look for a session library. Plenty out there, one heavyweight being Zend(http://framework.zend.com/manual/1.12/en/zend.session.html). You can save to a database instead for protected server-side sessions. I made one similar to Kohana's.
(2) I didn't understand the "another way" part - how does it differ from the method you described at first?
First method is to just add a token to your form and look for the token to have the expected value upon submission. If the form doesn't have it, you throw an error complaining.
Second method dynamically sets the field names upon form generation AND adds a token field. Submitting the proper form data from a program, bot, or outside source now first requires fetching the form since they wont know what field names to use (instead of just posting data with set field names).
"(3) most important, I am less worried about CSRF attacks, I just don't want bots/crawler to crawl into my forms, would this method prevent it from them, as opposed to humans? why? and is there an easier method to achieve that?"
If you mean bots like Google/SEO/respectful web-crawlers, robots.txt exists
for this purpose. robots.txt is a very simple text file that is placed in your site's root directory. You'll see requests in your webserver's access logs for a /robots.txt. This file tells search engine and other robots which areas of your site they are allowed to visit and index. You can read more on the (Robot Exclusion Standard)4 on many (websites)5.
As the second link notes, don't use robots.txt to hide information. It is a public file and visible to anyone. Also, malicious bots wont respect the file.
I'm not sure if when you say bots you mean just crawlers or spambots (bots trying to submit data) and such. If it's crawlers, robots.txt takes care of them. If it's spambots, you can add a hidden field (hidden with CSS not html) with a common name that when filled out you know is invalid, you can add a captcha, etc, etc, etc.
Try doing the tracking on the callback of the original request to ensure its loaded?
Also you could look into something like ajaxFormPlugin by malsup
i would like to suggest to check the permission of your "b.php" page. Please make sure the page has "w" permission for all users. this is a chance for not making a "POST" request.
I know it's a workaround but if, as I suppose, you have a bunch of checks for the $_POST variables, if you receive a GET request you could try replace the POST with the GET:
if (empty($_POST) && !empty($_GET)) $_POST = $_GET;
//here the check of $_POST
//...
since we don't know why this ipads (...apple -.-) have the issue, and between GET and POST there isn't so much difference - at least if you don't need to upload files...
The only way a post form can be sent as get is using script (changing the method attribute directly, or replacing the form behavior for example with an ajax request, binding to the event "submit" another function), so I suggest you to check every script that run in the parent and the children pages.
your ajax call doesn't contain method: "POST". This can be the cause.

How to find the URL of an external site's Javascript submit

I will do my best to try to explain this.
I am scraping a website for it's elements to then output in a different format. The problem that I am experiencing is the way that this site directs the user throughout the site is through a Javascript redirect.
When checking the 'a href' tag, this is the Javascript that shows up
javascript:doParamSubmit(2100, document.forms['studentFilteredListForm'], 'SSC000001MU9lI')
The SSC000001MU9lI changes for each element that it redirects to.
Is it possible to find a URL using this Javascript, so that I can reach the HTML page externally?
EDIT: Here is the doParamSubmit and doSubmit classes:
function doParamSubmit(event, form, parameter) {
form.userParam.value = parameter;
doSubmit(event, form);
}
function doSubmit(event, form)
{
// Make sure if something fails that the form can be resubmitted
try
{
// If this form has not been submitted yet... (except for IE)
if (allowSubmit == true && form != null && (submitted == false || isInternetExplorer6() || isInternetExplorer7()))
{
submitted = true;
form.userEvent.value = event;
// Fix for IE bug in which userEvent becomes a property array.
if (form.userEvent.length)
{
form.userEvent[0].value = event;
}
// Disable the form so the user can't accidentally resubmit the page
// (NOTE: this doesn't disable links (e.g. <a href="javascript:...">)
disableForm(form);
// If there is a populate form function, call it. If there are spell check fields on the
// page, populateForm is used to set hidden field values.
if (this.populateForm)
{
populateForm();
}
saveScrollCoordinates();
// resetSessionTimeout();
try
{
form.submit();
}
catch(e)
{
// Exceptions thrown here are only caused by canceling the submit in onbeforeunload, so ignore.
submitted = false;
}
}
if (allowSubmit == false)
{
alert(grabResource("message.pageLoading"));
}
}
catch(e)
{
submitted = false;
throw e;
}
}
I see 2 approaches.
You use a javascript enabled browser such as http://nrabinowitz.github.io/pjscrape/. I am not sure if you intend to just follow the links or instead grab the URL for some other use so your mileage may vary.
Find the doParamSumit() function in their page/scripts and analyze it to understand how it gets the URL - the one you have as an example looks like it grabs the action from a form perhaps? Once you know how the function work you might be able to harness that info in your scraping by using some regex to find URLs that match the doParamSubmit pattern and going from there. It's hard to say without seeing the function itself as well as the other links like it though.
Regardless of which method you choose I would begin by understanding the function - look for it in the code or loaded js files (you can also you things like javascript debuggers on most browsers to help you find it) and see what happens - it might be super obvious.
Also keep in mind that this might be a POST for a form - in which case the result of you following that link may not work if it expects valid form data.
Edit I see that you posted the function. It simply submits the form listed in the second parameter i.e. 'studentFilteredListForm'. While I don't think your scraping will go to far chasing forms you can still get the URL either with javascript if your scraper lets you (something like $('form[name=studentFilteredListForm]').attr('action') or using whatever language your are using for the scraper i.e. find the form and extract the action url (remembering that if there is no action it is probably posting back to the current URL)
But again... you might first manually get the URL of the form and see where that gets you. You might just get a page with form errors :)

Can't get a JS var inserted into a hidden field

I promise...I have researched this for over 24 hours. I know it is similar (if not exactly the same) as some other questions. I'm obviously missing something. (This is going to be a long-winded explanation...I apologize. Please don't let it scare you off...I'm pretty sure the root problem/question will be much easier than what all this looks like!)
End result desired is to get some JS vars POSTed over to my process PHP script from an HTML form. Simple, right?
I have a fully working form that does a bunch of calculations with a bunch of input fields...it's way too complex to post here...not to mention I really don't want to embarrass myself with what I'm sure is very bad coding!
In a nutshell...I have a function that in the process of running sets various JS vars. I need to get these vars inserted into some hidden input fields so that they will get moved over to my processing PHP along with all of the "normal" inputs. All of these vars represent floating point numbers (if that makes any diff)
Here is my form definition:
<form id="multiForm" action="App_post.php" method="POST" action="javascript:void(0)" id="appform" name="appform">
I have a "master" function that starts all of the calculations primarily based on when the user selects various radio buttons. Various selectors are dynamically updated in the form as the user selects buttons (which in reality is a user selecting different options of club membership fees and "add-on" items...someone is bound to ask what I'm doing!)
$(function () {
...lots of JS code here along with the calculations that create the JS vars I need
}
(I assume this function is called via the action="javascript:void(0)" )
Let's focus on just one var right now. The JS var "regularfee" (along with all the others I want) exists after all of the calculations are done. I need to have these values plugged into the hidden fields so that they can get POSTed.
Here is an example of the hidden field:
<input type="hidden" name="regularfee" id="regularfee" value="">
Here is where I am getting lost. I have done a console.log(regularfee) in the function right after all the calculations are done...it is set to the float number 26.5 (which is correct)
Here are a couple of things I have tried so far:
document.getElementById('regularfee').value=regularfee;
// --OR--
document.getElementById('regularfee').value=regularfee.value;
document.getElementById("app").submit();
No matter what I've tried, I get blank values POSTED over to my process PHP. Based on the other questions I've seen, the hidden input field method appears to be one of the "accepted" ways of doing this. I am not stuck on this method...just looking for the easiest way to get my vars POSTed! What am I missing? All help extremely appreciated!
Try regular post or use AJAX to SUbmit.
$.fn.serializeObject = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
This method gets all input in ur form including select and checked.
//multiForm form is your ID in ur form
var data = $("#multiForm :input").serializeObject();
after use ajax to save in php script.
$.ajax({
type: "POST",
url: "phpscript.php",
data: data,
success: function() {
alert("Input Success!");
}
});
How this will help you!
Technically, this syntax should be good to go...
document.getElementById('regularfee').value=regularfee;
This is assuming that the regularfee variable exists and it is of type float. Or even better use jQuery
$('#regularfee').val([your value here]);
Question, why do you have two actions in your form? How does it work?
<form id="multiForm" action="App_post.php" method="POST" action="javascript:void(0)" id="appform" name="appform">
Also, for peace of mind, also do a console.log to check the value of the hidden field after it's been set
You have two action attributes in your form. Remove the second one for "javascript:void(0)". It is not calling the function you think it is (the anonymous function you have there looks to be executed on dom ready).

collect data from a form hosted on another site

We have a number of clients that have agreed to send us their form data once a form is submitted on their site. Is this possible and what is the best way to handle this? Our site is built in coldfusion while the client site varies.
I had the client add a script tag to include a javascript file from our server on their form page. Also had them add an onClick event to their form button so this javascript is called on submission of their form.
This is the javascript file:
function cpcshowElements(f) {
var formElements = "";
for (var n=0; n < f.elements.length; n++) {
box = f.elements[n];
formElements += box.name + ":" + f.elements[n].value + ",\n";
}
var track = new Image();
/*send data to us*/
track.src="http://XXX.net/form_record.cfm?form="+ formElements + "&self=" + this.location;
}
On form submission the cpcshowElements function is called, formats the form data, appends it to the end of the XXX.net/...and calls that url. The form_record.cfm page basically does some checks and inserts the data into a table.
This process does work, however not consistently. The data doesn't always make it into the database. That is the problem. Is there another way to do this that won't have data loss?
The data getting to the database is pretty deep down the chain. The first step is to figure out where the request isn't coming through. Find the weak link, and then fix that part.
Chances are, there are other issues causing the failure than this piece of javascript. Test each part of the process and figure out where the problem lies. Chances are, it isn't in the javascript.
Check whether the form on the serve is being submitted by method other than onClick. If the form can be submitted by hitting enter or tabbing and hitting enter or the spacebar, than you are missing some submits. Would work more consistently with onSubmit rather than onClick.
Example:
<form onsubmit="your_function_here">
Also, if the form is submitting and then moving on to another page, you javascript code may not have enough time to fire. In that case, put a delay into your function to allow the GET request for the image to be made before the page evaporates.

How to continue running execution after form submit?

I am writing a simple form submit function for a specific site.
The script fills the specific form on the current page and submits the form. It MUST also fill the second form which appears after the first form is submitted and submit this form too.
That is there are 2 forms that must be filled and submitted.
Problem however, is that on a normal page the script fills and 1st form and submits it. After submission however, the script stops working! I want to continue execution!
I've done it by mistake on a page that had 2 frames! most pages don't have frames!
function FillFirstForm(){
doc=document;
//Some code removed from above...Fill Values
doc.forms[0].name.value = answerarray[1];
doc.forms[0].address.value = answerarray[2];
doc.forms[0].phone.value = answerarray[3];
doc.forms[0].username.value = answerarray[4];
//And Press Button
doc.forms[0].submit.click();
iTimer=setInterval(FillSecondForm,5000);
return true;
}
function FillSecondForm(){
clearInterval(iTimer);
doc.forms[0].tags.value = answerarray[5];
doc.forms[0].reference.value = answerarray[6];
document.forms[0].submit.click();
return true;
}
The basic rule is that a classical form submission halts all execution of scripts of the page, and loads the next page. No way to do anything about that.
You may have to switch to sending your form's contents using Ajax. That will leave the current page alive, and allow you to do as many submits as you want to.
A very easy way to achieve this is the jQuery form plugin. It takes much of the manual hassle out of the process.
Can you call FillSecondForm() function on body load of second form, once first form submitted.
You could POST the first form with AJAX, and then submit the second form after receving the response for the first form.
Something like this perhaps:
function FillFirstForm() {
var post_data = 'name='+escape(answerarray[1]);
post_data += '&address='+escape(answerarray[2]);
post_data += '&phone='+escape(answerarray[3]);
post_data += '&username='+escape(answerarray[4]);
// TODO: make this cross browser :)
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if ( req.readyState == 4 ) { // The request has finished
if ( req.status == 200 ) {
FillSecondForm();
}
else {
// Deal with errors here
}
}
};
req.setRequestHeader("Content-type","application/x-www-form-urlencoded");
req.open('POST', '/submit_url', true);
req.send(post_data);
}
W3 schools has a tutorial on AJAX here: http://www.w3schools.com/ajax/ajax_xmlhttprequest_create.asp, and there are many more on the web.

Categories

Resources