Reshowing database results in inputs when linked back to the page - coldfusion

I am using ColdFusion 16, HTML5, JavaScript and MSSQL for my project.
I have a form with Name, Email, Billing and Shipping Address and Phone number that once the form is submitted it inserts that info into the database. On another page I have a link to go back to that same page in case the user wants to change the info.
How do I display what is in the database back in those fields?
Also how do I have the page look exactly the same as from when it was left.
For example the box checked with the arrow saying billing and shipping is the same. Or have my hidden fields being shown if they were being shown when the page was submitted.
<div class="row">
<div class="col-lg-4">
<div class="form-group">
<label for="firstname">Name of owner:</label>
<cfoutput><input type="text" class="form-control input-sm" name="firstname" id="firstname" placeholder="First" maxlength="100" required="yes" value="" /></cfoutput>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label> </label>
<cfoutput><input type="text" class="form-control input-sm" name="middlename" id="middlename" placeholder="Middle" maxlength="100" value="" /></cfoutput>
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label> </label>
<cfoutput><input type="text" class="form-control input-sm" name="lastname" id="lastname" placeholder="Last" maxlength="100" required="yes" value="" /></cfoutput>
</div>
</div>
</div>

When you're on your Address page, you can use a query to populate the page.
You'll need some sort of id to verify the record you want to edit. I'm assuming the shipping record will be entered based on some sort of session identifier.
My theoretical database setup:
BillingAddress
| ID | Address1 | etc... |
===============================
| 1 | 123 Sesame St | ...... |
ShippingAddress
| ID | Address1 | etc... |
===============================
| 1 | 123 Sesame St | ...... |
On the initial form, we can query the database to see if there are any records for the user.
<cfquery name="getShippingAddress" datasource="myDSN">
SELECT TOP 1 ID, Address1, etc...
FROM ShippingAddress
WHERE ID = <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
</cfquery>
<cfquery name="getBillingDetails" datasource="myDSN">
SELECT TOP 1 ID, Address1, etc...
FROM BillingAddress
WHERE ID = <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
</cfquery>
NOTE: If we can be assured that there will never be more than one row for an ID, then we can skip the TOP 1.
When we write out our form, we can output existing values into the form.
<cfoutput>
<input type="text" name="shippingAddress1"
value="#encodeForHTMLAttribute(getShippingAddress.Address1)#" />
</cfoutput>
In the form, there is a checkbox (BillingShippingSame) to determine if the Billing Address is the same as the Shipping Address.
We can set this value with some additional checks.
<cfif BillingShippingSame = getShippingAddress.Address1 = getBillingAddress.Address1 AND .... >
This just says that if the Address fields in both queries are the same, then set BillingShippingSame to true otherwise false. If all of the fields in both queries are the same, you can also convert both queries to JSON and compare them. (serializeJSON(getShippingAddress) == serializeJSON(getBillinAddress)). This will save some typing if you have multiple fields that need to be checked, but they'll only serialize the same if the fields in both queries are the same. Then just set the value in you checkbox input.
<input type="text" name="BillingShippingSame" <cfif BillingShippingSame>checked="checked"</cfif> />
And if you wanted to, you could javascript the page so that if BillingShippingSame is checked, it either disables the Billing form fields, or just continues to populate them with what was returned from the database.
Back to our workflow. Since HTML won't pass a checkbox if it isn't checked, then just look in the form scope to see if that box was checked. If it was, then set the Billing details to the Shipping details.
Insert/Update queries:
<cfset cleanShippingAddress1 = cleanString(form.ShippingAddress)>
<cfif structKeyExists(form,"BillingShippingSame")>
<cfset cleanBillingAddress1 = cleanString(form.ShippingAddress)>
<cfset cleanOtherBillingFields = .....>
<cfelse>
<cfset cleanBillingAddress1 = cleanString(form.BillingAddress)>
<cfset cleanOtherBillingFields = .....>
</cfif>
<cfset cleanOtherFields = cleanString(....)>
<cfquery name="UpsertShippingAddress" datasource="myDSN">
UPDATE ShippingAddress
SET ShippingAddress1 = <cfqueryparam value="#cleanShippingAddress1#" cfsqltype="cf_sql_varchar">
, OtherShippingFields =
<cfqueryparam value="#cleanOtherShippingFields#" cfsqltype="cf_sql_varchar">
, ...
WHERE ID = <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
IF ##ROWCOUNT=0
INSERT INTO ShippingAddress ( ID, ShippingAddress1, OtherShippingFields, ....)
VALUES (
<cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
, <cfqueryparam value="#cleanShippingAddress1#" cfsqltype="cf_sql_varchar">
, <cfqueryparam value="#cleanOtherShippingFields#" cfsqltype="cf_sql_varchar">
, ...
) ;
</cfquery>
<cfquery name="UpsertBillingAddress" datasource="myDSN">
UPDATE BillingAddress
SET BillingAddress1 = <cfqueryparam value="#cleanBillingAddress1#" cfsqltype="cf_sql_varchar">
, OtherShippingFields =
<cfqueryparam value="#cleanOtherBillingFields#" cfsqltype="cf_sql_varchar">
, ...
WHERE ID = <cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
IF ##ROWCOUNT=0
INSERT INTO BillingAddress ( ID, BillingAddress1, OtherBillingFields, ....)
VALUES (
<cfqueryparam value="#session.ID#" cfsqltype="cf_sql_integer">
, <cfqueryparam value="#cleanBillingAddress1#" cfsqltype="cf_sql_varchar">
, cfqueryparam value="#cleanOtherBillingFields#" cfsqltype="cf_sql_varchar">
, ...
) ;
</cfquery>
I tend to err on the side of paranoia when it comes to form or url or other untrustworthy inputs. My cleanString function would do various sanitations on the string before it goes into the database.
I also used a SQL pattern that UPDATEs the database, and if no rows were inserted (##ROWCOUNT=0 >> ID wasn't found), then it will INSERT instead.

Related

Coldfusion: update table based on form selection

I have a form which allows users to assign multiple records to a technician by Area number. It does this by outputting a line for each area in the outstanding records with a select list of the joined technician:
<cfoutput query="getAreaList">
<div class="row">
<div class="col-xs-4">
<div class="form-group">
<label for="hub">DWS Area:</label>
<input type="text" name="oparea" id="oparea" value="#OpArea#" class="form-control" />
</div>
</div>
<div class="col-xs-4">
<div class="form-group">
<label for="sub">Assign to:</label>
<select id="series" name="sub" class="form-control">
<cfloop query="getTechAreas">
<cfif OpArea EQ getAreaList.OpArea>
<option value="#username#" class="#OpArea#">#Displayname#</option>
</cfif>
</cfloop>
</select>
</div>
</div>
</div>
</cfoutput>
The resultant output looks like this - two lists of data:
form.opArea: Area 4,Area 5
form.sub: PERSON1,PERSON2
My aim is to update my jobs table, where each row for the specific form.OpArea is updated with the name of the technician, form.sub.
How do I loop through each list and insert the correct tech name where the opArea is matched?
I have tried looping through the insert but the whole of the list is being inserted, not the currently evaluated list item. I'm currently just trying to insert this into a holding table to test my code:
<cfoutput>
<cfloop list="#form.oparea#" index="i">
<cfquery datasource="cfLKDM" name="insertdata">
insert into [DWS_General_Dev].[LogSure].[Holding]
(AreaNo)
values
('#form.oparea#')
</cfquery>
Thank you
You are getting the whole list because you are still referencing the form field form.oparea within your loop. Which contains the entire list. When looping over a list, as you are doing, the cfloop will populate the index variable with the current value of the list item during each iteration.
So instead of using form.oparea you should be using i in your example. You should change the name of that index variable to be more descriptive.
Like this for your example:
<cfloop list="#form.oparea#" index="i">
<cfquery datasource="cfLKDM" name="insertdata">
insert into [DWS_General_Dev].[LogSure].[Holding]
(AreaNo)
values
('#i#')
</cfquery>
Better yet change the index variable name to be more descriptive:
<cfloop list="#form.oparea#" index="thisOPArea">
<cfquery datasource="cfLKDM" name="insertdata">
insert into [DWS_General_Dev].[LogSure].[Holding]
(AreaNo)
values
('#thisOPArea#')
</cfquery>

Delete items unchecked from a form using ColdFusion

When a user deselects an item (by unchecking the checkbox) from the form, I want the corresponding data to be deleted from the database.
Here is what my form looks like:
Category
Item 80 626
1.Item # 1 (87) chk chk
2.Item # 2 (59) chk chk
So basically, the list looks like this:
DB_list = (80|87, 626|87, 80|59, 626|59)
Now if a user deselects one of these items 80|87, I want it to be deleted from the database like so:
DELETE FROM TBL
WHERE Item = 87
AND Category = 80
So on form submit, the list becomes
New_List = (626|87, 80|59, 626|59)
How do we delete 80|87?
This is one way to do it:
<cfoutput>
<cfset DB_list = "80|87,626|87,80|59,626|59" />
<cfset New_List = DB_list />
<cfset myitems="80,626">
<cfset mycategories = "87,59"/>
<cfif structKeyExists(form,"fieldnames")>
<!-- on submit -->
<cfif structKeyExists(form,"listitem")>
<cfset New_List = form.listitem>
</cfif>
<br />RESULT:<br />
DB_list:#DB_list#<br />
New_List:#New_List# <br /><br />
<cfloop from="1" to="#ListLen(DB_list)#" index="ix">
<cfset listitem = ListGetAt(DB_list,ix)>
<cfif ListFindNoCase(New_List,listitem) eq 0>
<!-- hence: split in javascript -->
<cfset listitemarray=ListToArray(listitem,"|")/>
<cfset itemid=listitemarray[1] />
<cfset categoryid=listitemarray[2] />
<cfquery>
DELETE FROM TBL
WHERE Item = <cfqueryparam cfsqltype="CF_SQL_INTEGER" value="#itemid#">
AND Category = <cfqueryparam cfsqltype="CF_SQL_INTEGER" value="#categoryid#">
</cfquery>
</cfif>
</cfloop>
</cfif>
<form name="tstForm" id="tstForm" method="post" action="">
<table>
<tr>
<td> </td>
<cfloop from="1" to="#ListLen(mycategories)#" index="lstindex1">
<td>Category #ListGetAt(mycategories,lstindex1)#</td>
</cfloop>
</tr>
<cfloop from="1" to="#ListLen(myitems)#" index="lstindex2">
<tr>
<cfset itemid=#ListGetAt(myitems,lstindex2)# />
<td>item #itemid#</td>
<cfloop from="1" to="#ListLen(mycategories)#" index="lstindex3">
<cfset catid=#ListGetAt(mycategories,lstindex3)# />
<td>
<input type="checkbox" id="listitem_#itemid#_#catid#" name="listitem" value="#itemid#|#catid#" <cfif ListFindNoCase(New_List,"#itemid#|#catid#",",") gt 0>checked</cfif>/>
</td>
</cfloop>
</tr>
</cfloop>
</table>
<button type="submit">Submit</button>
</form>
</cfoutput>
Run this code here
My approach would be to have a hidden field in the form that has all the available values for the checkboxes. Then when the form is submitted, compare what's in the hidden form field with what's in the checkbox field. Whatever the hidden field has that the check box field does not can be deleted.
Without the use of JavaScript, Dan Bracuk's solution would work the best for your stated requirements. However, another simpler approach to this problem is to just delete all previous choices then re-insert whatever items are still checked. Then there is no need to compare old and new values to determine what has changed.
How about
DELETE
FROM tbl
WHERE NOT CONVERT(varchar(20), Item) + '|' + CONVERT(varchar(20), Category)
IN <cfqueryparam cfsqltype="CF_SQL_varchar" value="#form.new_list#" list="yes">
My approach using sql server is to delete all the list items from the db, and then add only the ones that have been submitted by the form. All delete and add queries should be put into a single transaction, so if anything fails you can make o rollback.

Where to put a cfquery in a Coldfusion Form

I have a cfquery that is using some of FORM fields in the WHERE clause. My first problem is that every time I access my webpage the cfquery code appears on the top of the page. Where should I put the query within the .cfm form and access some of the fields within the form? My second problem is I'm not sure that the WHERE clause is recognizing the values for the fields. Can you help me please?
Here is the way my code is set up:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script language="javascript" type="text/javascript">
function addRow() {
var tbl = document.getElementById('tblSample');
var lastRow = tbl.rows.length;
var iteration = lastRow - 3;
var row = tbl.insertRow(lastRow);
........... etcetra.......
}
</script>
</head>
<body lang=EN-US style='tab-interval:.5in'>
<div class=Section1>
<cfparam name="awardTotals" default="0" />
<cfparam name="search_award.GrandTotal" default="0" />
<cfif isDefined("form.Finalize")>
<cfquery name="search_award" datasource="Test">
SELECT g.Code1 + g.Code2 + g.Code3 + g.Code4 AS GrandTotal
FROM Codes g
WHERE g.CodeNumber = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.CodeNum#">
AND g.TestYear = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.TestYear#">
AND g.SelType = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.SelType#">
AND g.Jurisdiction = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.SelJuris#">
</cfquery>
<cfif not search_award.GrandTotal is FundingTotals>
<script type="text/javascript">
alert('The totals do not match.');
return false;
</script>
<cfelse>
<script type="text/javascript">
alert('The totals match.');
return true;
</script>
</cfif>
</cfif>
<p class=MsoNormal align=center style='text-align:center'><
<cfoutput>
<form name="thisform" action="FormData.cfm" method="post">
<p class=MsoNormal align=left style='text-align:left'>
<input type="hidden" id="totalFields" name="totalFields" value="0">
<input type="text" id="awardTotals" name="#search_award.GrantTotal#" value="0">
<table width="1177" border="1" id="tblSample">
<tr style='mso-yfti-irow:0;mso-yfti-firstrow:yes'>
<th height="10"bgcolor="##cccccc" colspan="10"><h3>Jurisdiction:
<select name="SelJuris" id="SelJuris">
<option value = "0">---Jurisdictions---</option>
<option value = "1">Allegany County</option>
<option value = "2">Anne Arundel County</option>
<option value = "3">Baltimore County</option>
<option value = "4">Calvert County</option>
<option value = "5">Caroline County</option>
</select>
<input name="CodeNum" id="CodeNum" type="text" size="20">
<input name="TestYear" id="TestYear" type="text" size="20">
<input name="SelType" id="SelType" type="text" size="20">
</th>
</tr>
</table>
<input type="Submit" name="Submit Form" value="Submit Form" onClick="">
<input type="Button" name="Finalize" value="Finalize" onClick="">
</form>
</body>
</cfoutput>
</html>
(Update from comments)
Right now I'm getting a message that Element CODENUM is undefined in FORM
You want this sort of logic. If the form has been submitted, do something with it. In your case, it would be like this:
<cfif structkeyexists(form, "codenum")>
query, process, display, etc
<cfif>
rest of page
Also, you want to use query parameters for a variety of reasons. So this:
g.CodeNumber = '#form.CodeNum#'
becomes this:
g.CodeNumber = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.CodeNum#">
unless it's a number in which case you change the datatype.
You need add a condition around your cfquery. Also update the name of your submit button without spaces
<input type="Submit" name="SubmitForm" value="Submit Form" onClick="">
<cfif isDefined("form.SubmitForm")>
<cfquery name="search_award" datasource="TrenaTest">
SELECT g.Code1 + g.Code2 + g.Code3 + g.Code4 AS GrandTotal
FROM Codes g
WHERE g.CodeNumber = '#form.CodeNum#'
AND g.TestYear = '#form.TestYear#'
AND g.SelType = '#form.SelType#'
AND g.JurisdictionID = '#form.SelJuris#'
</cfquery>
</cfif>
Please note that in all the examples, a standard html form tag is being used, but the condition for the query is defined in the Coldfusion form scope. I believe you would need to convert the <form> to <cfform> and <input> to <cfinput> like:
...........
<cfinput type="Submit" name="SubmitForm" value="Submit Form" onClick="">
</cfform>
<cfif isdefined('form.submit')>
.............

Setting a form variable inside the form (coldfusion)

I am trying to manually set a variable INSIDE a form, because it contains html and placing it in the value attribute of a tag would cause errors in the display. Currently, I check to see if that attribute contained html, and if so, the field is empty.
I'd like to be able to set the variable as the old value if it contained html in previous entries of the form, so that the user wouldn't have to enter that field every time they loaded that ORM object to edit.
Here's a snippet:
<cfif ("#dataobject.getField()#" NEQ "" AND Left(dataobject.getField(), 1) EQ "<")>
<cfscript>
temp = dataobject.getField();
temp2=temp;
temp2 = Insert("---", temp2, 0);
temp2 = Insert("<!", temp2, 0);
temp2 = Insert("--->", temp2, Len(temp2));
dataobject.setField(temp2);
</cfscript>
<label for="name">
Field:
</label>
<input type="text" name="Field" value="">
<button id="savefield" name="savefield">Save</button>
<cfif form.Field EQ ""><cfset form.Field = temp></cfif>
<cfscript>
dataobject.setField(temp);
</cfscript>
<cfelse>
<label for="name">
Field:
</label>
<input type="text" name="Field" <cfif ("#dataobject.getField()#" NEQ "")>value="#dataobject.getField()#"</cfif>>
<button id="savefield" name="savefield">Save</button>
</cfif>
The code I was trying to use:
<cfif form.Field EQ ""><cfset form.Field = temp></cfif>
Coldfusion throws an error saying that the FORM variable is undefined (which doesn't surprise me). The "savefield" button calls javascript that opens a window allowing the user to set the value, then closes. Should I put my code there instead?
-The inserts which turn the string into a comment was an early attempt at a workaround that didn't work :/
Ok hopefully the following pointers will help you going:
Verify that the field exists in the form via; structKeyExists(form, "field") or use cfparam to initialize a default value
Escape the value with HTMLEditFormat(dataObject.getField()) to escape any HTML code which breaks the html
So e.g.;
<cfparam name="form.field" default="" />
<label for="field">
Field:
</label>
<input type="text" id="field" name="field" value="#htmlEditFormat(form.field)#" />
<button id="savefield" name="savefield">Save</button>
Gl !
if you want to force a value into the form (or any) scope you'll want to use cfparam before you use it.
<cfparam name = "form.field" default = "">
<cfif form.Field EQ "">
<cfset form.Field = temp>
</cfif>
Essentially this is the same thing as
<cfif !structKeyExists(form,"field")>
<cfset form.field = "">
</cfif>
Don't forget that preservedata is your friend.
You don't have to worry about the value="" attribute if you populate the form field ahead of time.
<cfquery name="qry">
SELECT Field1,Field2
FROM table
WHERE ID=<cfqueryparam cfsqltype="cf_sql_integer" value="#url.ID#">
</cfquery>
<cfloop list="#qry.Columnlist#" index="FieldName">
<cfset form[FieldName] = HTMLEditFormat(qry[FieldName][1])>
</cfloop>
<cfform preservedata="yes">
<label for="Field1">Field One:</label>
<cfinput name="Field1">
<label for="Field2">Field Two:</label>
<cfinput name="Field2">
</cfform>

Value for my session variable?

Here my code to create a session variable:
<cflock timeout="999" scope="Session" type="Exclusive">
<cfset Session.IDUsers = "">
</cflock>
I need to put a value in = "" but I want the value to change to be the users ID depending on which user is logged in. So I can't just put any number etc. in there. What do I have to do?
Basically, you're going to do this when you log your user in. A sign-in routine might look like the following:
<cfquery datasource="cfgossip" name="signin">
SELECT
ID_USERS
FROM
USERS
WHERE
UN = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.un#" />
AND PW = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.pw#" />
</cfquery>
<cfif signin.recordCount>
<cfset session.idUsers = signin.id_users />
</cfif>
Until they've logged in, you can't know what the value you need is. However, once you've determined that they are who they say they are and you're going to let them in, you can then set the session variable.
Ok I think you need a basic breakdown of how this should work.
Step 1: User goes to a section of your website/app that needs login. You check if the session.userid is set to a valid value. If not goto login screen.
Step 2: Present user with a login form.
<form action="checklogin.cfm">
<input type="text" name="username" value="">
<input type="password" name="pass" value="">
<input type="submit" value="login">
</form>
Step 3: On clicking login the form is submitted to an action page which checks if the credentials supplied match a valid user.
<cfquery datasource = "myDB" name = "getUsers">
SELECT userID
FROM USERS
WHERE username = <cfqueryparam cfsqltype = "cf_sql_varchar" value = "#form.username#" />
AND password = <cfqueryparam cfsqltype = "cf_sql_varchar" value = "#form.pass#" />
</cfquery>
Step 4: If valid user goto the logged in area else return to login screen
<cfif getUsers.recordCount GT 0>
<cfset session.IDUsers = getUsers.userID />
<cflocation url="home page for logged in users">
<cfelse>
<cflocation url="return to login form and display invalid login message">
</cfif>
This is a very basic login form but it should get you started.