I have a multi-page form. Visit page 1, page 2 and then page 3. Push refresh (f5) and the form goes back to page 2.
This is with drupal-6. The problem looks similar to this http://drupal.org/node/1060290.
Digging into the problem, via the form_cache database table. Both page 1 and 2 data appear in there. In the php debugger it looks as though a new form_id has been created. ie. storage_form-1add3819cbea88139679819935a69686 is the key in the database cache table and form-bcf9556f57f5352a57dfbba4c2120ee7 is the 'form_id' on refresh.
What does my form code look like?
Main form function:
function myform_online(&$form_state) {
// $form_state['storage']['step'] keeps track of what page we're on.
// start at step 1 if no storage has been set
if (!isset($form_state['storage']['step'])) {
$form_state['storage']['step'] = 1;
}
// If we are saving the form data we should submit rather than display the details.
// At least look at saving the step.
// Don't lose our old data when returning to a page with data already typed in.
$default_values = array();
if (isset($form_state['storage']['values'][$form_state['storage']['step']])) {
$default_values = $form_state['storage']['values'][$form_state['storage']['step']];
}
switch ($form_state['storage']['step']) {
case 1:
// Your Details
module_load_include('inc', 'join_online', 'includes/step1');
And we handle submit:
function join_online_submit($form, &$form_state) {
//Save the values for the current step into the storage array.
//dsm($form_state);
$form_state['storage']['values'][$form_state['storage']['step']] = $form_state['values'];
# ahah - bail.
if ($form_state['ahah_submission']) {
return;
}
// How do we work out if this was a refresh? It currently does start with 1 and think that the step is #2.
//Check the button that was clicked and change the step.
if ($form_state['clicked_button']['#id'] == 'edit-previous') {
$form_state['storage']['step']--;
} elseif ($form_state['clicked_button']['#id'] == 'edit-next') {
$form_state['storage']['step']++;
} elseif ($form_state['clicked_button']['#id'] == 'edit-finish') {
//You should store the values from the form in the database here.
//We must do this or the form will rebuild instead of refreshing.
unset($form_state['storage']);
//Go to this page after completing the form.
$form_state['redirect'] = 'join_online/form/thank-you';
}
}
If you use $form_state['rebuild'] = TRUE in _submit function the state of the form is saved and can be used for default values.
Check this example:
http://www.ferolen.com/blog/how-to-create-multistep-form-in-drupal-6-tutorial/
Related
I have a web page that allows authorized users to update or delete rows in a MySQL table. The table has the columns id (INT), label (VARCHAR), details (VARCHAR), templateId (INT), and auditable (TINYINT, either 0 or 1). This table is displayed as HTML on the front end, with "Label", "Details", "Auditable?" and "Edit/Delete" columns showing.
Clicking the "Edit" button on a row, changing some data in the resulting Bootstrap modal form, and clicking "Save Changes" works fine. The problem occurs when I click "Edit", click "Cancel" on the form, and then click another row (say, for example, I accidentally clicked the wrong row). When I click the button to execute the edit on that newly clicked row, both that row and the initially clicked row will be affected. The Chrome console shows that two JavaScript objects are being sent via $.post(), though I cannot figure out why from the logic I wrote (see below). I looked in MySQL and the duplicate results are there, confirming the page accurately reflects the update. Is there some $.get or $.post cache behavior in jQuery I am not aware of? (This also occurs with the delete functionality, but I'm limiting the question for brevity's sake).
Main page (GUI):
// The 'edit this row' button that brings up the modal form
$(".edit-action").click(function() {
// Clear any previously set values in form
$("#editActionLabel").val('');
$("#editActionDetails").val('');
$(".radio-edit-action").prop("checked", false);
// All edit button id's in GUI table will be "edit-action-[`id` in database]"
// Grab number out of there and convert from string to number
var actionId = $(this).attr("id");
actionId = parseInt(actionId.slice(12));
// Grab data from database to populate fields
$.get("data.php?a=actionData&actionId=" + actionId, function(d) {
// Returning a JSON encoded array isn't working,
// so I'm sending back a comma-separated string instead
var response = d.split(",");
var label = response[0];
var details = response[1];
var auditable = parseInt(response[2]);
$("#editActionLabel").val(label);
$("#editActionDetails").val(details);
if (auditable == 1) {
$("#editAuditableNo").prop("checked", false);
$("#editAuditableYes").prop("checked", true);
} else if (auditable == 0) {
$("#editAuditableYes").prop("checked", false);
$("#editAuditableNo").prop("checked", true);
}
// Only reset `auditable` variable if selection was changed
$(".radio-edit-action").change(function() {
auditable = $(this).val();
auditable = parseInt(auditable);
});
// User clicks "Save Changes" instead of "Cancel"
$("#executeEdit").click(function() {
var label = $("#editActionLabel").val();
var details = $("#editActionDetails").val();
var obj = {
"operation": "edit",
"actionId": actionId,
"label": label,
"details": details,
"auditable": auditable
};
console.log("The object passed to 'edit' this row:");
console.log(obj);
$.post("data.php", obj, function(r) {
// Confirm success or failure to user
$("#crudResult").html(r);
});
}); // end click
});
}); // end 'edit action'
data.php (called via AJAX to execute the UPDATE in database. Only relevant code shown):
$debug = false;
$operation = $_POST['operation'];
$action_id = (isset($_POST['actionId']) ? $_POST['actionId'] : '');
$label = (isset($_POST['label']) ? $_POST['label'] : 'NULL');
$details = (isset($_POST['details']) ? $_POST['details'] : 'NULL');
$auditable = (isset($_POST['auditable']) ? $_POST['auditable'] : 'NULL');
switch ($operation) {
case 'edit':
$query = "
UPDATE actions
SET label='$label',
details='$details',
auditable=$auditable
WHERE id=$action_id
LIMIT 1";
// DB connection not shown. Yes, I know I should be using PDO...
$result = mysqli_query($db_conn, $query);
// PHP echoes out the result; let the calling JavaScript figure out where to place it
if ($result) {
echo '<p class="text-success">Action successfully updated!</p>';
} else {
echo '<p class="text-warning">There was an error and the action could not be edited.</p>';
// Don't show error to user in production, when $debug should be false
if ($debug) {
echo '<p><b>Error:</b> ' . mysqli_error($db_conn) . '</p>';
}
}
break;
/* case 'delete': ... */
}
The modal form, which follows Bootstrap's template HTML, is simply a collection of fields and a couple buttons (no <form> wrapped around it). Please let me know if I can clarify anything.
How many times is the request to server occurs? I Bet it is twice.
The problem is on the client side.
For each edit click you have made, a new save click function has been created.
You do not need to add this for every edit click, take it out from the edit click function.
Hope it helps.
I'm hoping I'm on the right track here....
I have some elements on my page (tables).. that are dynamically generated based on the results of querying a DB.... (I add inside of a container DIV)..
inside these tables are some text..and a handful of checkboxes... each table is the same (outside of the value of the text fields)..
When a user clicks on a checkbox.. I add an element to another container DIV off to the side.
If a user un-checks the checkbox.. it removes the element from the container DIV on the side. On each 'click' event..... I am also either adding or removing the 'selections' from an ARRAY (and also updating this array to my PHP SESSION)..
When the user is done.. they click a button and go to another page.. where this SESSION array is grabbed and reviews/summarizes their 'choices'..
*there is no FORM tags.. checkboxes are free-form in the tables (not wrapped in any FORM tags..so there is NO general POST action to grab everything.. hence the use of an array/SESSION)
If the user goes BACK to the original 'selection page' (with the tables/checkboxes)..
I am re-populating the PAGE (both re-checking any checkboxes...and re-populating the elements in the container DIV to the side.. all based on the SESSION data)
In each checkbox.. I am adding a little PHP function to write in checked="checked" or not.. when the checkboxes instantiate)
like so:
<label><input id="articlesnaming" name="Articles Naming Expert" type="checkbox" value="0.00" <?=sessionCheck($row["id"] ."-A","Articles Naming Expert") ?> onclick=""/> Articles Naming Expert</label>
FYI: on the function being called:
function sessionCheck($recordID, $checkBoxID){
if(isset($_SESSION['userPicks']) && count($_SESSION['userPicks']) != 0){
for($r = 0; $r< count($_SESSION['userPicks']); $r++){
if($_SESSION['userPicks'][$r]['recordid'] == $recordID){
for($z=0; $z < count($_SESSION['userPicks'][$r]['itemsordered']); $z++){
if($_SESSION['userPicks'][$r]['itemsordered'][$z]['name'] == $checkBoxID){
return 'checked="checked"';
}else if($z == (count($_SESSION['userPicks'][$r]['itemsordered']) - 1)){
return "";
}
}
}else if($r == (count($_SESSION['userPicks']) - 1)){
return "";
}
}
}else{
return "";
}
}
Everything up to this point works fine...
Its when I go to dynamically build/add (append) those elements in the container DIV on the side... where problems happen.
I am getting them added just fine and when a user RE-VISITS the page.. previous checkboxes they had selected were/are checked again... -and-.. the elements ARE in the container DIV to the side of the stage/screen)...
PROBLEM: When I un-check one of the checkboxes, it DOES NOT remove the element in the container DIV on the side? I have to re-click the checkbox..(which adds a duplicate).. then I can un-check it.. but it only removes the NEW one..
Everything seems to work fine until a refresh/re-visit of the page (and I have to automatically populate the checkboxes and the elements in the container DIV on the side).. then the checkboxes stop behaving/interacting with the elements that were adding through another function (still same ID's...paths..from what I can tell)....and -not- added through an initial checkbox event/action..
I am grasping at straws here.... it is perhaps because I'm using a PHP function to set the checkboxes on refresh? and it maybe doesn't know its current state? (although the visual state of the checkbox is accurate/correct)
Any ideas are appreciated.
Code used to set/un-set checkboxes & add/remove elements from the side container DIV :
<script>
//var to hold current check box clicked
var targetCheckbox;
//var to hold cumulative total
var totalPrice = 0;
//array to keep track of user picks from returned record results
//try to get SESSION array (if available/set) from PHP into jQuery using json_encode()
<?php if(isset($_SESSION['userPicks'])){ ?>
//overwrite jQuery userPicks MAIN array
var userPicks = <?php echo json_encode($tempArray) ?>;
<? }else{ ?>
//create new jQuery userPicks MAIN array, and populate through user clicks/interaction
var userPicks = [];
<? } ?>
$(document).ready(function() {
//check to see if seesion and populate checks and side column from previous picks
//if existing session, loop through and populate the CHOICES column
if(userPicks.length > 0){
console.log("SESSION EXISTS, POPULATE CHOICES COLUMN FROM ARRAY");
for(i=0; i<userPicks.length; i++){
//build up sub array data first then append at one time.
var subArrayLength = userPicks[i].itemsordered.length;
var subArray = '';
for(s=0; s<subArrayLength; s++){
subArray += '<li id="' + userPicks[i].orderid + userPicks[i].checkboxid + '">' + userPicks[i].itemsordered[s].name + '</li>';
}
$("#choicesWrapper #itemList").append('<div class="recordChoices"><h5>CASE NAME: '+userPicks[i].casename+'</h5><ul id="'+userPicks[i].recordid+'">'+subArray+'</ul></div>');
}
}
//onClick event
$('.orderOptions').on('click', 'input:checkbox', function () {
//routine when checkbox is checked
if ($(this).is(":checked")) {
$(this).prop("checked", true);
console.log("checked");
//console.log('doesnt exist..create it');
$("#choicesWrapper #itemList").append('<div class="recordChoices"><h5>CASE NAME: '+caseName+'</h5><ul id="'+resultsID+'"><li id="'+orderID+targetCheckbox+'">'+itemOrdered+'</li></ul></div>');
}else{
$(this).prop("checked", false);
console.log("un-checked");
//remove the option from right column (li element)
console.log("REMOVE TARGET: #choicesWrapper #itemList #"+resultsID+" "+orderID+targetCheckbox);
$("#choicesWrapper #itemList #"+resultsID+" #"+orderID+targetCheckbox).remove();
//check if no more children and remove parent/title (record container/div)
if ($("#choicesWrapper #itemList #"+resultsID+" li").length > 0) {
//console.log("Still has children...do nothing");
}else{
//console.log("No Children...");
$("#choicesWrapper #itemList #"+resultsID).parent().remove();
}
}
}
}
</script>
Oddly enough, when things are 'auto-populated' from the SESSION data (like on refresh or re-visiting the page) and when things 'break', unchecking the checkboxes doesn't remove things, but when I uncheck the very last checkbox in a group, it does remove the parent (so that parent removal code/routine is being executed...but not then child )
I'm thinking this is a pathing issue? (I believe I am creating things with exactly the same ID's/classes...etc).
Definitely worth the +1 if you answer! :)
The only other thing I can think of is.. HOW the userPicks array gets created.. initial visit to page, I just create an empty JS/jQuery array and wait to push/populate it when a user clicks a checkbox (code above for onClick stuff).
But when a user visits the page (refresh or re-visit) and -HAS- (previous) SESSION data still available.... then I grab the PHP SESSION array.. and pass it to jQuery using json_encode()...
Do I need to add/delete from that array differently than I do if I created normally?
I want to redirect to a page after executing a php function and also submit a html form with the methode POST, at once.
I found many solutions with GET but I want to handle this with POST.
You can use cUrl to send POST data you want, then make the redirect.
Look on the net for: "php curl".
Make your form action point to the php document where you want to execute your function and then in the end place this header("location: your_location_file.php");
Step one - submit form to functions.php
Step two - do what ever you need to do with the submited data
Step three - Redirect
Example:
<form method="post" action="functions.php">
...
</form>
functions.php
<?php
...
all your code
...
header("location: your_location_file.php");
?>
Javascript can help if you don't want to rely on curl. Had this laying around. Pass in $_POST or an array of the data you want posted. Add error/parameter checking.
function http_post_redirect($url='', $data=array(), $doc=true) {
$data = json_encode($data);
if($doc) { echo "<html><head></head><body>"; }
echo "
<script type='text/javascript'>
var data = eval('(' + '$data' + ')');
var jsForm = document.createElement('form');
jsForm.method = 'post';
jsForm.action = '$url';
for (var name in data) {
var jsInput = document.createElement('input');
jsInput.setAttribute('type', 'hidden');
jsInput.setAttribute('name', name);
jsInput.setAttribute('value', data[name]);
jsForm.appendChild(jsInput);
}
document.body.appendChild(jsForm);
jsForm.submit();
</script>";
if($doc) { echo "</body></html>"; }
exit;
}
You could use a session to hold the POST data.
I am currently using code like below. On my first page load, the $_POST data is checked. If it contains certain values already in the database, then it redirects to a page for those values.
// This could be part of the same script as below, or a different script.
session_start();
if($_POST['my_value'] && valueExistsInMyDb($_POST['my_value']) ) { // check my db to see if this is an existing value
$id = getIdOfMyValue($_POST['my_value']); // e.g. '4'
$_SESSION['POST'] = $_POST; // take ALL post data and save it in the session variable
header("location: your.php?myvalue=" . $id); // redirect to bookmarkable target page where $_GET variable matches what was posted.
exit(); // ensure no other code is executed in this script after header is issued.
}
Then your other file (or maybe even the same file) could do this:
// your.php?myvalue=4
if(isset($_SESSION) && array_key_exists('POST',$_SESSION)) {
$_POST = $_SESSION['POST']; // creates or overwrites your $_POST array with data from the session. The rest of your script won't be able to tell that it's not a real $_POST, which may or may not be what you want.
unset($_SESSION['POST']); // you probably want to remove the data from the session.
}
// now your myvalue=4 is stored in GET, and you can handle the rest of the POST data as you like
I don't know if that is the best solution, but so far it seems to be working for me so far. I only just wrote the code a few days ago and haven't tested all aspects yet.
Another option is to use HTML5 to change the address bar. No redirect needed. But the downside is that only "modern Webkit browsers" can use it, apparently.
I have 2 pages :
page1.php :
- has a form with text box and a "submit" button. Eg : <form name="frm_register" action="page1.php" method="post">
- php and mysql code to store the value of textbox to database. Javascript will redirect the page to php2.php after the value is submitted to database. Eg :
$query = "INSERT INTO traceuser (username) VALUES ('{$username}')";
$result = mysql_query($query, $connection);
echo '<script language="javascript">window.location="page2.php";</script>';
page2.php
- mysql retrieve the data from database and display on this page.
Problem : When I press "back" button, the browser will pop up a warning message saying that the form will be resubmit. How to prevent resubmit the form when click "back" button? Is it I need to clear the cache of page1.php? How to do it with php or javascript or ajax?
Update 1 : Thanks for the answer of replacing javascript window.location="page2.php" to php header('Location: home2.php');. It fix 80% of problem. The rest of 20% problem show below :
if (strtotime($_SESSION['servertime']) < time()-3){ //10800 = 3 hours 3600 = 1 hour
if (($username != "") AND ($username != $_SESSION[username])){
$_SESSION['servertime'] = $servertime;
$_SESSION['username'] = $username;
$query = "INSERT INTO traceuser (username) VALUES ('{$username}')";
$result = mysql_query($query20, $connection);
header('Location: page2.php');
exit;
} else {
echo "same name"; //problem here
}
}else{
echo "submit multiple data too fast"; //problem here too.
}
}
The problem happen when do the following steps :
1) User submit data successfully, jump to page2.php view records.
2) User click "back" button, jump back to page1.php.
3) User submit data fail, stay on page1.php. (because too fast or same name)
4) User submit data successful, jump to page2.php view records.
5) User click "back" button, but browser shows warning message "form will be resubmited".
The problem is because of Step 3. Step 3 didn't run header('Location: page2.php');, didn't jump to page2.php. So it cause Step 5 show the warning message. How to fix this problem?
Update 2 : I have figured out the solution to fix the 20% problem, it works perfectly. I use session['error123'] to decide whether or not want to display the error message "same name". I kill session['error123'] if success submit data to database or if success jump to page2.php. I also use header('Location: page1.php'); to redirect to own page (same page) to make the page forget about form submission previously. Example of codes :
if ($_SESSION['error123'] == "toofast"){
echo $_SESSION['error123'] ;
}elseif ($_SESSION['error123'] == "samename"){
echo $_SESSION['error123'] ;
}
if (strtotime($_SESSION['servertime']) < time()-3){ //10800 = 3 hours 3600 = 1 hour
if (($username != "") AND ($username != $_SESSION['username'])){
$_SESSION['username'] = $username;
$query = "INSERT INTO traceuser (username) VALUES ('{$username}')";
$result = mysql_query($query20, $connection);
$_SESSION['error123'] = "aa";
header('Location: http://localhost/plekz/page2.php');
exit;
} else {
$_SESSION['error123'] = "samename";
header('Location: http://localhost/plekz/page1.php');
exit;
}
}else{
$_SESSION['error123'] = "toofast";
header('Location: http://localhost/plekz/page1.php');
exit;
}
}
}
Note : You need to buffer the output by <?php ob_start();?> because $_SESSION cannot put before header(). Buffer will stop all output including session, let header() send the output first.
Rather than
echo '<script language="javascript">window.location="page2.php";</script>';
you should use the header() function to redirect your user after the submission.
So in psuedo code,
click submit on page.php action page1.php
page1.php submits data to database calls
header('Location: http://example.com/page2.php');
This should prevent your clicking back problem
You can prevent the re-submission by implementing the Post-Redirect-Get (PRG Pattern).
Could be just a one-line if you've got the http_redirect function:
http_redirect("page2.php");
Instead of your javascript echo.
If not, that are two lines:
header("Location: http://example.com/page2.php");
exit;
Replace example.com with site's your hostname.
Related: Back button re-submit form data ($_POST); I am confused about PHP Post/Redirect/Get
One way is to submit the Formdata via Ajax to a remote Script and if the Query returns success you can jump the a "Thank You" Page.
So the User can hit the Back Button and the "Reload" Request doesn't pop up.
Hope the Idea helps you
Can you do it via an Ajax call instead? No action on the form, and the submit will call a the Ajax function. The Ajax call will execute the query, and provide a response (you can just echo a result), and you can then provide dynamic feedback based on the result. You'd never leave the page.
<form id="thisForm">
...form input fields...
<input type="button" onclick="return submitForm('thisForm')"/>
</form>
function submitForm(formId) {
$.ajax( {
type: "post",
url: 'page2.php',
data: $('#' + formId + ' input').serialize(),
... any other Ajax parameters...,
success: function(data) {
}
});
return false;
}
Add this code in the page that is showing as offline when the user clicks the back button:
<?php
session_start();
header_remove("Expires");
header_remove("Cache-Control");
header_remove("Pragma");
header_remove("Last-Modified");
?>
Create a Session like shown here
You should use session and validate the user from every page and you will amaze how SESSION works! AMAZING!
Here is my scenario, I am creating a form with codeigniter, I understand how to populate the fields with models and such. I have the layout of the form. It is now running from my index function. I want to store all the data given to that form and access them in a postdata array with each index being the name of the value. Please help. CodeIgniter, PHP
you create the form
echo form_open('mycontroller/mymethod');
// rest of form functions
or <form name="myform" method="post" action="<?php echo site_url('mycontroler/mymethod');?>" > // rest of html form
then, in Mycontroller:
function mymethod()
{
$postarray = $this->input->post();
// you can pass it to a model to do the elaboration , see below
$this->myloadedmodel->elaborate_form($postarray)
}
Model:
function elaborate_form($postarray)
{
foreach($postarray as $field)
{
\\ do your stuff
}
}
If you want XSS filtering you can pass a TRUE as second parameter in the $this->input->post() call. Check user guide on input library
See code igniter's input class
One exemple of how to form your code would be:
public function add_something()
{
if (strtolower($_SERVER['REQUEST_METHOD']) == 'post') { // if the form was submitted
$form = $this->input->post();
// <input name="field1" value="value1 ... => $form['field1'] = 'value1'
// you should do some checks here on the fields to make sure they each fits what you were expecting (validation, filtering ...)
// deal with your $form array, for exemple insert the content in the database
if ($it_worked) {
// redirect to the next page, so that F5/reload won't cause a resubmit
header('Location: next.php');
exit; // make sure it stops here
}
// something went wrong, add whatever you need to your view so they can display the error status on the form
}
// display the form
}
This way your form will be displayed and if submitted its content will be processed, if an error occurs you will be able to keep the submitted values to pre-enter them in the form, display an error message etc ... And if it works the user is redirected to that he can reload the page safely without submitting multiple times.