Preserving input value of type="file" in ColdFusion on postback - coldfusion

I have a form in ColdFusion that initially has 5 input fields for file uploading. Should the user realize they have more than 5 files to upload in the process of attaching them, I would like the form to preserve the values when it submits itself for the change in # of fields.
Using the <cfform> tag with the preservedata="yes" attribute is supposed to accomplish this - but all I'm getting is a temp value stored in the input's value on resubmit that isn't displayed in the field nor works for a submission.
edit: Thanks for the great answers everyone, you all helped and were correct. I was able to implement Adam's suggested solution. Works great! Thanks!
function changeFieldCount() // javascript function for submit on input count change
{
var count = document.forms[0].numtotal.options[document.forms[0].numtotal.selectedIndex].value;
document.forms[0].action = "me.cfm?count=" + count;
document.forms[0].submit();
}
<cfparam name="url.count" default="5">
<cfform name="dispfiles" method="post" enctype="multipart/form-data" preservedata="yes">
<!--- looping for file input fields --->
<cfloop index="counter" from="1" to="#url.count#">
<cfinput type="file" name="file#counter#" size="50" maxlength="60"><br>
</cfloop>
<br>Number of Files to Attach:
<!--- looping for # of input selection--->
<cfselect name="numtotal">
<cfloop index="cnt" from="1" to="20" step="1">
<cfoutput>
<option value="#cnt#" <cfif #cnt# eq #url.count#>selected</cfif>>
#cnt#
</option>
</cfoutput>
</cfloop>
</cfselect>
<cfinput type="button" name="op-display" value="Display" onclick="changeFieldCount()">
<cfinput type="button" name="op-upload" value="Attach Files" onclick="submitfrm(this.form)">
<cfinput type="button" name="cancel" value=" Cancel " onclick="window.close()">
</cfform>
This is what I'm getting when I view source on the resulting submission:
<input name="file1" id="file1" type="file" value=".../cfusion.war/neotmp8858718543274653167.tmp" maxlength="60" size="50" /><br>

This is by design in all browsers for security reasons. There is no way you can insert the value for a file field.

To elaborate on #SpliFF's answer, what you need to do is dynamically create more file fields with JavaScript.
Here's an example that uses jQuery to make the JavaScript a little easier. I've also used a variable to track the number of file fields displayed so that they all have a unique number appended to their names, making it possible to loop over and uniquely identify them server-side.
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
//track the current number of file fields displayed
var numUploadFields = 5;
//attach an anonymous function to the button to add more fields
$(function(){
$("#add5").click(function(){
for (var i = 0; i < 5; i++){
numUploadFields += 1;
var newHTML = "<br/><input type='file' name='upload" +
numUploadFields + "' />";
$("#someSection").append(newHTML);
}
});
});
</script>
<form method="post" action="">
<div id="someSection">
<input type="file" name="upload1" /><br/>
<input type="file" name="upload2" /><br/>
<input type="file" name="upload3" /><br/>
<input type="file" name="upload4" /><br/>
<input type="file" name="upload5" />
</div>
<input type="button" id="add5" value="Add 5 more file fields" />
</form>

Build additional file inputs using JS DOM methods. Since you aren't leaving the page this is fast and nothing is lost.

Related

Radio button not checked breaks page but works if checked

I have a simple form that I'm trying to pass data to which on the action page has an email that sends the data that I put into the form.
On my form page. I have a text field and two radio buttons.
My text field works if I don't put data into it. However, on my radio buttons the page works if I check it but doesnt work if I don't check it at all which results in my page breaking I'm not sure what I'm doing wrong? I want the page to process the default to "no" if I didn't check either radio buttons. Do I have to validate or something?
Here is my code.
<cfparam name="form.firstName" default="">
<cfparam name = "form.optradio1" default="no">
<form action="test.cfm" method="post">
<label for="firstName"></label>
<input type="text" name="firstName">
<input type="radio" name="optradio1" Value="Male" <cfif form.optradio1 eq "Yes">checked</cfif>>
</form>
That's how radio and checkbox inputs work in HTML. If they are not checked, they are not submitted on the form submit.
To determine if the radio input was checked, you can use
structKeyExists(form, <name of the input, as string>) as in
structKeyExists(form, "optradio1").
<cfparam name="form.firstName" default="">
<form action="test.cfm" method="post">
<label for="firstName"></label>
<input type="text" name="firstName">
<input type="radio" name="optradio1" Value="Male" <cfif structKeyExists(form, "optradio1")>checked</cfif>>
</form>
Assuming you have two radio inputs:
<cfparam name="form.firstName" default="">
<form action="test.cfm" method="post">
<label for="firstName"></label>
<input type="text" name="firstName">
<input type="radio" name="optradio1" Value="Male" <cfif structKeyExists(form, "optradio1") and form.optradio1 eq "Male">checked</cfif>>
<input type="radio" name="optradio1" Value="Female" <cfif structKeyExists(form, "optradio1") and form.optradio1 eq "Female">checked</cfif>>
</form>
Your initial code doesn't work because:
if checked, form.optradio1 was equal to Male
if not checked, form.optradio1 was defaulted to no because of
<cfparam name = "form.optradio1" default="no">

select statement displays full list but not in a select statement

I am struggling to find out where I have gone wrong, and have spent hours trying to find out why this is happening, I think its just something I have missed but I cant spot it.
What happens is that the select statement instead of showing a dropdown which i can just select a team to delete displays all of the teams in a list with a delete button next to it, yet when I use the select statement on another page it works fine. if anyone can point out where I have gone wrong that would be appreciated
Cheers
<cfquery name="deleteteam" datasource="danny2">
SELECT *
FROM pool_teams
</cfquery>
<html>
<head>
<title>LCF Delete Team</title>
</head>
<body>
<cfif IsDefined('Form.delete_button')>
<cfoutput>
<form action="#CGI.SCRIPT_NAME#" method="post">
<input type="hidden" name="ID" value="#FORM.ID#"/>
do you really want to delete record?
<input type="submit" name="confirm_button" value="Yes">
<input type="submit" name="cancel_button" value="No">
</form>
</cfoutput>
<cfelseif IsDefined('FORM.confirm_button')>
<cfquery datasource="danny2">
DELETE FROM pool_teams
WHERE ID = '#FORM.ID#'
</cfquery>
The record has been deleted
<cfoutput> Return to list</cfoutput>
<cfelseif IsDefined('FORM.cancel_button')>
<cflocation url="#CGI.SCRIPT_NAME#" >
<cfelse>
<cfoutput query="deleteteam">
<form action="#CGI.SCRIPT_NAME#" method="post">
<select>
<option value="#ID#">#teamname#</option>
</select>
<input type="hidden" name="ID" value="#deleteteam.ID#">
<input type="submit" name="delete_button" value="delete"/>
</form>
</cfoutput>
</cfif>
</body>
</html>
<cfoutput query="deleteteam">
<form action="#CGI.SCRIPT_NAME#" method="post">
<select>
<option value="#ID#">#teamname#</option>
</select>
<input type="hidden" name="ID" value="#deleteteam.ID#">
<input type="submit" name="delete_button" value="delete"/>
</form>
</cfoutput>
This is where you are going wrong. If you think of outputting a query as performing a loop. each time you loop over the query you are making another form with one select option in it.
You should change your code to look something like this.
<form action="#CGI.SCRIPT_NAME#" method="post">
<select>
<cfoutput query="deleteteam">
<option value="#deleteteam.ID#">#deleteteam.teamname#</option>
</cfoutput>
</select>
<input type="hidden" name="ID" value="#deleteteam.ID#">
<input type="submit" name="delete_button" value="delete"/>
</form>
What My code is doing is just adding options for each query item not the complete from.
Hope that makes sense.

How can I keep form values without using session?

I have one form page with pagination. I want to keep the form values as the user goes to the previous or next page, using pagination. I know that it can be done using the session scope. However, here I don't want to use session scope. Does anyone have any ideas on how to do this without using session? Please let me know.
Here is my form page:
<cfoutput>
<form action="#buildUrl(action='survey.save_surveyresults',querystring='surveyId=#rc.surveyid#')#" method="post">
<input type="hidden" name="id" value="0">
<input type="hidden" name="surveyid" value="#rc.surveyId#">
<div class="container-fluid">
<div class="row">
<div class="control-group">
<label class="label-control" for="name">Name</label>
<div class="controls">
<input type="text" name="name" id="name" required="true" placeholder="enter your name" value="#rc.name#">
</div>
</div>
<div class="control-group">
<label class="label-control" for="email">Email</label>
<div class="controls">
<input type="text" name="email" id="email" required="true" placeholder="enter your Email" value="#rc.email#">
</div>
</div>
<cfloop query="rc.questions" startrow="#startrow#" maxrows="#perpage#">
<!--- because we have all questions and answers in query we can use switch instead calling template or view
for each question, so its simplify directory structures, questions directory is not necessary now --->
<h3>#CurrentRow#<cfif rc.questions.isrequired><strong>*</strong></cfif>. #rc.questions.question#<h3>
<cfswitch expression="#rc.questions.template#">
<fieldset>
<cfcase value="textbox">
<input type="text" class="input-xlarge" name="#template#_#questionid#" id="question_#questionid#">
</cfcase>
<cfcase value="multiplechoice">
<cfloop list="#answer#" delimiters="," index="i">
<div class="controls">
<label>
<input type="radio" name="#template#_#questionid#" id="question_#questionid#" value="#answerID#" >
<span class="lbl">#i#</span>
</label>
</div>
</cfloop>
</cfcase>
<cfcase value="multiplechoiceother">
<cfloop list="#answer#" delimiters="," index="i">
<div class="controls">
<label>
<input type="radio" name="#template#_#questionid#" id="question_#questionid#" value="#answerID#" >
<span class="lbl">#i#</span>
</label>
</div>
</cfloop>
<div class="control-group">
<label class="label-control" for="other">Other</label>
<div class="controls">
<input type="text" class="input-xlarge" name="#template#_#questionid#" id="question_#questionid#">
</div>
</div>
</cfcase>
</fieldset>
</cfswitch>
</cfloop>
<p></p><br />
<cfif startrow GT 1>
Previous
</cfif>
<cfif (startrow + perpage - 1) lt rc.questions.recordcount>
Next
<cfelse>
<button type="submit" name="submit" class="btn btn-success">Finish</button>
</cfif>
</div>
</div>
</div>
</form>
</cfoutput>
You could break the form up into different sections and have it all in one page. You can hide/show parts of the form using JavaScript based on which 'page' of the form they are one.
This makes going forward or backward in the form a snap since it is not submitted until they are done with the whole form and the values they entered will still be there.. and is pretty easy to handle with jQuery or other JavaScript libraries.
As Dan said - save submitted values in hidden fields.
One issue I see with your HTML is that Previous/Next pages are just links - not submit buttons. So make sure that when clicking those links users are submitting the form - not just going to a different url.
Here's a simple snippet of code that will embed all your form variables into hidden fields. You would place this code inside the form handler on the page you are submitting to. Note Lucas' answer as well. Your form may not be submitting correctly for reasons of badly formed..er...form.
<Cfloop collection="#form#" item="fItem">
<cfoutput>
<input type="hidden" name="#fItem#" value="#form[fItem]#"/>
</cfoutput>
</cfloop>
Again .. this would go _inside" of the form on the subsequent page. This is fairly common in multipart forms (shopping carts with multiple steps, profile entries etc).
Bear in mind that with the approaches above, you need to re-validate your form values on the server side every time you submit them (or at the very least before your final processing).
What you make up for in server memory, you may lose in terms of traffic and load times, depending on scale so I would advise that you proceed with caution. Increasing production traffic unnecessarily can result in financial impacts, and often server memory can be cheaper than extended increased traffic outlay; it comes down to your requirements and scale at the end of the day.
Shipping form variables around also increases your attack surface for malicious injection of form data, so while you may be concerned with session variables being altered on you (curious to hear more on this), you are already opening yourself up by shipping this data around as plain text. Do not rely on client-side validation for this (or any) data.

Accessing variables of a dynamic form

I am creating a form with cfloop and need to access each variable individually when submitting the form. I need to use the selected entries of the form in a loop that will add my selections to a database.
This is my form:
<form method="post">
<input type="hidden" name="isPost" value="1">
...
<cfoutput>
<cfloop query="client_admin_surveys">
<input type="text" size="35" name="surveyID" id="surveyID" value="#id#">
<input type="text" size="35" name="surveyName" id="surveyName" value="#name#">
<input type="checkbox" name="amplify" id="amplify">
<input type="checkbox" name="enchance" id="enchance">
<input type="checkbox" name="pacify" id="pacify">
<input type="checkbox" name="pacifyUrgent" id="pacifyUrgent">
</cfloop>
</cfoutput>
...
<input type="submit" name="submit" value="Submit">
</form>
After posting the form, the results group all of my selections because I have the same "name" for my form elements. I tried adding an i count next to each name to make it different but then I got a bit confused about how to process the fields.
You started down the correct path when you added the counter - go back and add that, something like:
<input type="checkbox" name="amplify#client_admin_surveys.currentRow#" id="amplify">
Would work.
I also sometimes like to add a form field for the 'counter' on the processing page
<input type="hidden" name="counter" value="#client_admin_surveys.recordCount#" />
Then on the processing page, you can loop over the counter and access the form fields using bracket notation
<cfloop from="1" to="#form.counter#" indexd="i">
<cfset thisAmplify = form["amplify" & i] />
<cfset thisEnhance = form["enhance" & i] />
<!---- more logic here --->
</cfloop>

Invoking a CFC from a FORM

Like the title says, I am invoking a CFC from a FORM. This is how I am doing it now, but there has to be a better way.
Form:
<cfform action="choice.cfm" method="post">
<cfinput type="hidden" name="method" value="DeleteMessage">
<cfinput type="hidden" name="announcement" value="#announcement#">
<cfinput type="hidden" name="ID" value="#ID#">
<cfinput type="submit" value="Delete" name="form.OKbutton1" onclick="return confirm('Are you sure you want to delete?');">
</cfform>
Action page:
<cfif IsDefined("form.OKbutton1")>
<cfinvoke component="pdprojects.scr.changedisablesysequipment"
method="DeleteMessage"
returnvariable="DeleteMessages"
argumentCollection="#Form#" />
</cfif>
I figured out a way to streamline this with jquery and cfc. Page updates fast, and no worries if the person hits back button or refreshes the page. You could probably do a lot more with JQuery to append a div tag instead of location.reload(); (Page Refresh) at the end of the call.
CF Call
<cfajaxproxy cfc="pdprojects.scr.changedisablesysequipment" jsclassname="choice_cls">
Here is the jquery
<script src="/js/jquery-1.7.1.min.js">
<script>
$(document).ready(function(){
$("#OKbutton").click(function(){
if ($('#Announcement').val() == "") {
alert("Please enter message in the text area!");
}
else {
var instance = new choice_cls();
instance.setForm("enterit");
instance.InsertMessage();
location.reload();
}
});
$("#OKbutton1").click(function(){
var instance = new choice_cls();
instance.setForm("deleteit");
instance.DeleteMessage();
location.reload();
});
});
Here is my html
<cfform action="" id="enterit" name="enterit" method="post">
<cfinput type="hidden" value="#session.username#" name="thisname">
<cftextarea cols="50" rows="8" name="Announcement"></cftextarea><br>
<cfinput type="button" value="Enter Announcement" name="OKbutton" id="OKbutton">
</cfform>
<cfform action="" id="deleteit" name="deleteit" method="post">
<cfinput type="hidden" name="method" value="DeleteMessage">
<cfinput type="hidden" name="announcement" value="#announcement#">
<cfinput type="hidden" name="ID" value="#ID#">
<cfinput type="button" value="Delete" name="OKbutton1" onclick="return confirm('Are you sure you want to delete?');">
</cfform>
Then just create your CFC for coldfusion!