So I've been using in-line (correct term?) ColdFusion code to run all my pages and have gotten to a point where I think I have a decent understanding of the basics, and want to take the next step. After a lot of cross-referencing, research, and trial and error, I've come up with the following 4 pages of which the intent is to be able to enter a username and password in the form page (crud.cfm), then, after submission, redirect the user to a page which displays the newly entered data, as well as any past entries.
I can do all of this with simple in-line code and an Application.CFM but I want to migrate toward a more OOP/Modular approach going forward, as presently I find myself rewriting/copy-pasting scads of code across several different pages. The error that I get when I submit from 'crud.cfm' is:
"The component attribute in cfinvoke tag has invalid value."
I have tried it without hashes and without the uppercase "A" to no avail. Here is my non-working code:
Application.cfc
<cfcomponent output="false">
<cffunction name="insertrecord" access="public" output="false">
<cfargument name="data" type="struct" required="true">
<cfquery name="create" datasource="test">
INSERT INTO logins(
username,
password)
VALUES(
'trim(form.username)',
'trim(form.password)')
</cfquery>
</cffunction>
</cfcomponent>
crud.cfm
<h3> Enter new user/password </h3>
<cfform name="thisform" method="post" action="procpage.cfm">
Username:<cfinput type="text" name="username" value="">
Password:<cfinput type="password" name="password" value="">
<input type="submit" value="submit">
</cfform>
procpage.cfm
<cfif !StructIsEmpty(form)>
<cfinvoke component="#Application#" method="insertrecord">
<cfinvokeargument name="data" value="#form#">
</cfinvoke>
</cfif>
<cflocation url="resultpage.cfm" addtoken="no">
resultpage.cfm
<cfquery name="read" datasource="test">
SELECT * FROM logins
</cfquery>
<table>
<tr>
<th>LOGIN</th>
<th>USERNAME</th>
<th>PASSWORD</th>
</tr>
<cfloop query="read">
<tr>
<td>#read.login#</td>
<td>#read.username#</td>
<td>#read.password#</td>
</tr>
</cfloop>
</table>
ColdFusion version 8, MSSQL 2005 database.
Thank you all in advance for your help, looking forward to your responses!
Application.cfc is a special file in ColdFusion.
http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=AppEvents_01.html
You'll need to name your component something else. Application.cfc is where you put Application events, setup code, etc.
In answer to your question to Miguel, Yes. The application.cfc is called each time a cfm is called. So when you hit the crud.cfm, application.cfc runs. When you hit procpage.cfm application.cfc runs etc..
so you want your procpage.cfm to look more like
<cfif !StructIsEmpty(form)>
<cfinvoke component="functions" method="insertrecord">
<cfinvokeargument name="data" value="#form#">
</cfinvoke>
</cfif>
and your functions.cfc looks more like
<cfcomponent output="false">
<cffunction name="insertrecord" access="public" output="false" returntype="void">
<cfargument name="data" type="struct" required="true">
<cfquery name="create" datasource="test">
INSERT INTO logins(
username,
password)
VALUES(
<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(form.username)#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(form.password)#">
)
</cfquery>
</cffunction>
<cffunction name="readRecord" access="public" returntype="query">
<cfargument name="loginID" type="numeric" required="false" default="0">
<cfquery name="read" datasource="test">
SELECT * FROM logins
where loginID = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.loginID#">
</cfquery>
<cfreturn read/>
</cffunction>
</cfcomponent>
resultsPage.cfm
<cfscript>
f = createObject('component','functions');
r= f.readRecord(theIdToPass);
</cfscript>
<table>
<tr>
<th>LOGIN</th>
<th>USERNAME</th>
<th>PASSWORD</th>
</tr>
<cfloop query="r">
<tr>
<td>#r.login#</td>
<td>#r.username#</td>
<td>#r.password#</td>
</tr>
</cfloop>
</table>
You will find that cfqueryparam not only has a small performance boost but will protect you from sql attacks and such, USE IT!! Always.
You also might think about doing arguments for each one expect instead of using struct variables, since writting error trapping for a struct without know what you are expecting to be passed can be pretty frustrating.
Hopefully this can get you off to a positive start with CF!
Related
I'm using CFML for my application. I need help with developing a logout operation that destroys a session. For now, on the logout link I'm calling the login page but when the BACK Button on the browser is clicked the user is still logged in.
<!---LoginForm.cfm>--->
<!---Handle the logout--->
<cfif structKeyExists(URL,'logout')>
<cfset createObject("component",'authenticationService').doLogout() />
</cfif>
<!---Form processing begins here--->
<cfif structkeyExists(form,'submitLogin')>
<!---Create an instane of the authenticate service component--->
<cfset authenticationService=createObject("component",'authenticationService') />
<!---Server side data validation--->
<cfset aErrorMessages=authenticationService.validateUser(form.userEmail,form.userPassword)>
<cfif ArrayisEmpty(aErrorMessages)>
<!---Proceed to the login procedure --->
<cfset isUserLoggedIn=authenticationService.doLogin(form.userEmail,form.userPassword) >
</cfif>
</cfif>
<!---Form processing ends here--->
<cfform>
<fieldset>
<legend>Login</legend>
<cfif structKeyExists(variables,'aErrorMessages') AND NOT ArrayIsEmpty(aErrorMessages)>
<cfoutput>
<cfloop array="#aErrorMessages#" index="message" >
<p >#message#</p>
</cfloop>
</cfoutput>
</cfif>
<cfif structKeyExists(variables,'isUserLoggedIn') AND isUserLoggedIn EQ false>
<p class="errorMessage">User not found.Please try again!</p>
</cfif>
<cfif structKeyExists(session,'stLoggedInUser')>
<!---display a welcome message--->
<p><cfoutput>Welcome #session.stLoggedInUser.userFirstName# </cfoutput>
<p>Logout</p>
<cfelse>
<dl>
<dt>
<label for="userEmail">Email address</label>
</dt>
<dd>
<cfinput type="email" name="userEmail" required="true" >
</dd>
<dt>
<label for="userEmail">Password</label>
</dt>
<dd>
<cfinput type="password" name="userPassword" required="true" >
</dd>
</dl>
<cfinput type="submit" name="submitLogin" value="Login" />
</fieldset>
</cfif>
</cfform>
<cfdump var="#session#">
<!---authenticationService.cfc--->
<cfcomponent>
<cffunction name="validateUser" access="public" output="false" returntype="array">
<cfargument name="userEmail" type="string" required="true" />
<cfargument name="userPassword" type="string" required="true" />
<cfset var aErrorMessages=ArrayNew(1) />
<!---Validate the email--->
<cfif NOT isValid('email',arguments.userEmail)>
<cfset arrayAppend(aErrorMessages,'Please,provide a valid email address') />
</cfif>
<!---Validating the Password--->
<cfif arguments.userPassword EQ ''>
<cfset arrayAppend(aErrorMessages,'Please, provide a password') />
</cfif>
<cfreturn aErrorMessages />
</cffunction>
<!---doLogin() Method--->
<cffunction name="doLogin" access="public" output="false" returntype="boolean">
<cfargument name="userEmail" type="string" required="true" />
<cfargument name="userPassword" type="string" required="true" />
<!---create the isUserLoggedIn variable--->
<cfset var isUserLoggedIn=false />
<!---get the user data from the database--->
<cfquery datasource="myapp" name="getInfo">
select * from Info
where emailid='#form.userEmail#' and password='#form.userPassword#'
</cfquery>
<!---Check if the query returns one and only one user--->
<cfif getInfo.recordcount eq 1 >
<!--- log the user in --->
<cflogin>
<cfloginuser name="#getInfo.username#" password="#getInfo.password#" roles="#getInfo.role#">
</cflogin>
<!--- save user data in session scope --->
<cfset session.stLoggedInUser={'userFirstName'=getInfo.username} />
<!---change the isUserLoggedIn variable to true--->
<cfset var isUserLoggedIn=true />
</cfif>
<!---return the isUserLoggedIn variable --->
<cfreturn isUserLoggedIn />
</cffunction>
<!---doLogout() Method--->
<cffunction name="doLogout" access="public" output="false" returntype="any">
<!---delete user from session scope--->
<cfset structDelete(session,'stLoggedInUser') />
<!---log the user out--->
<cflogout />
</cffunction>
</cfcomponent>
Regarding the back button after logout, the situation being that someone could log off and walk away from their computer without closing the browser or locking it. Then anyone else could go back on their browser and view the data they had been viewing before logging out.
We solved this for a financial application by implementing a Pragma: no-cache header on every page request. This forces requests to the page to reload from the server, not just load what's in the browser's cache. This means the back button will request the previous URL from the server, which will check session and kick you to your logged out landing page.
It will throw off some users who are used to navigating your site a certain way, but it will make it much more secure.
I'm working on a Coldfusion project and I seem to be stuck. I am a newbie so I hope I don't get too much slack. The purpose of my project is to create a password list by using nested loops. I am to design a template that combines all words in the list "cold, fusion, dynamic" with all words on the list "bert, ernie, oscar" to produce a bulleted list of vaild passwords. This template should process two URL prarameters named List1 and List2. I must use two list loops nested within each other to produce all possible combinations of words. (For example "coldbert", "coldernie", "coldoscar", "fusionbert" and so on..)
This is what I have so far:
<cfinclude template="header.cfm">
<body>
<h2>Loop List</h2>
<cfhttp url="looplist.cfm?List1=cold,fusion,dynamic&List2=bert,ernie,oscar" method="get">
<CFLOOP LIST="#URL.List1#"
INDEX="List1">
<UL><CFOUTPUT>#List1#</CFOUTPUT></UL><br>
</CFLOOP>
<cfinclude template="footer.cfm">
I want to ensure that I'm going in the right direction here. Thanks guys for any assistance.
Unless you're calling a page that doesn't exist on your site, I do not see a need to do a http call. You could just create a function in the template (although I'd prefer it to be in a separate cfc) and call that to get your password combos. Something like ...
<cffunction name="getPasswordCombos" returntype="string">
<cfargument name="list1" type="string" required="true" />
<cfargument name="list2" type="string" required="true" />
<cfset var passwordCombos = "" />
<cfset var i = "" />
<cfset var j = "" />
<!--- your combo generation logic might look something like --->
<cfloop list="#arguments.list1#" index="i">
<cfloop list="#arguments.list2#" index="j">
.....
<!--- set passwordCombos logic here --->
.....
</cfloop>
</cfloop>
<cfreturn passwordCombos />
</cffunction>
Then,
<cfset passwordCombos = getPasswordCombos("cold,fusion,dynamic", "bert,ernie,oscar") />
Then loop over the "passwordCombos"
<ul>
<cfloop list="#passwordCombos#" index="i">
<li>#i#</li>
</cfloop>
</ul>
Also, if you HAVE to user CFHTTP, use cfhttpparam to pass in arguments. It's much cleaner.
<cfhttp result="result" url="looplist.cfm" method="GET">
<cfhttpparam name="list1" type="url" value="cold,fusion,dynamic">
<cfhttpparam name="list2" type="url" value="bert,ernie,oscar">
</cfhttp>
For my autosuggest I'm using the code below.
When typing, it only looks at the beginning of the string.
For example if I type 'abcd' it will come with suggestions 'abcde...' that is good.
But what it doesn't do, if I type 'def' and in the database I have a string 'abcdefg' then this doesn't come in the autosuggest.
In my query I do have the percent characters infront and behind the search string.
So to me it looks like the autosuggest only looks at the characters typed at the beginning, not in the middle.
Or am I doing something wrong?
<cfcomponent output="false">
<cffunction name="lookupJobs" access="remote" returntype="array">
<cfargument name="search" type="any" required="false" default="">
<cfset var qGetJobs="">
<cfset var result=ArrayNew(1)>
<cfquery name="qGetJobs" datasource="#application.dsn#">
SELECT DISTINCT title
FROM tbl_jobs
WHERE title LIKE '%#arguments.search#%'
ORDER BY title
</cfquery>
<cfloop query="qGetJobs">
<cfset ArrayAppend(result, title)>
</cfloop>
<cfreturn result>
</cffunction>
</cfcomponent>
Thanks to Leigh, the solution was with the cfinput.
<cfinput type="text" name="title"
showautosuggestloadingicon="false"
autosuggest="cfc:autosuggest.lookupJobs({cfautosuggestvalue})"
matchContains="true">
I needed to add the attribute: matchContains="true"
I'm trying to use autocomplete with a field with commas in it. When I type the comma it will ignore it, and won't return anything. So far I have this:
index.cfm
<!--- A simple form for auto suggest --->
<cfform action="autosuggest.cfm" method="post">
Artist:
<cfinput type="text" name="artist" size="50" autosuggest="cfc:autosuggest.findartist({cfautosuggestvalue})" autosuggestminlength="4" maxresultsdisplayed="5" /><br /><br />
</cfform>
autosuggest.cfc
<cfcomponent output="false">
<!--- Lookup used for auto suggest --->
<cffunction name="findartist" access="remote" returntype="string">
<cfargument name="search" type="any" required="false" default="">
<!--- Define variables --->
<cfset var local = {} />
<!--- Query Location Table --->
<cfquery name="local.query" datasource="#application.datasource#" >
select DISTINCT artist
from items
where artist like <cfqueryparam cfsqltype="cf_sql_varchar" value="#ucase(arguments.search)#%" />
order by artist
</cfquery>
<!--- And return it as a List --->
<cfreturn valueList(local.query.artist)>
</cffunction>
</cfcomponent>
When I try to search for example Brown,James it doesn't return anything. What do I need to put in it to return results with commas.
Thanks
One option is to have your function return an array, instead of a string. Then delimiters are not an issue.
<cffunction name="findartist" access="remote" returntype="array">
...
<cfreturn listToArray(valueList(local.query.artist, chr(30)), chr(30))>
</cffunction>
Update:
As Raymond pointed out, the only sure-fire way to avoid delimiter issues on the CF side is not to use them. Instead loop through the query to build the array, ie:
<cffunction name="findartist" access="remote" returntype="array">
...
<cfset local.arr = []>
<cfloop query="local.query">
<cfset arrayAppend(local.arr, local.query.artist)>
</cfloop>
<cfreturn local.arr>
</cffunction>
I have a text field that I want to autosuggest values based on a query. I have a main file along with a separate file (getdata.cfc) that holds my query.
Here is the text field portion of my main file:
<cfinput name="search_query" autosuggest="url:getdata.cfc?suggestvalue={cfautosuggestvalue}" maxResultsDisplay="10" showAutoSuggestLoadingIcon="true" size="10" />
Here is the code in getdata.cfc:
<cfcomponent>
<cffunction name="get_data" access="remote" output="false">
<cfargument name="suggestvalue" required="true">
<cfquery name="get_data" datasource="#application.DSN#">
SELECT DISTINCT myItem
FROM myTable
WHERE myItem LIKE <cfqueryparam value="#suggestvalue#%"
cfsqltype="cf_sql_varchar">
ORDER BY myItem
</cfquery>
<cfif get_data.recordCount eq 1>
<cfreturn ",#get_data.myItem#">
<cfelse>
<cfreturn ValueList(get_data.myItem)>
</cfif>
</cffunction>
</cfcomponent>
The text field shows up fine, but when I type a word no autosuggest values show up. Nothing happens. The text is just displayed as I type it.
Any suggestions? Thank you!
I switched away to using jquery plugins from a lot of CF stuff, but here is an example I have that works in some old production code
<cfinput type="text" name="email" id="email" autosuggest="cfc:cfc.users.lookupEmail({cfautosuggestvalue})" maxresultsdisplayed = "25">
<cffunction name="lookupEmail" access="remote" returntype="array">
<cfargument name="search" type="any" required="false" default="">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(1)>
<!--- Do search --->
<cfquery name="data" datasource="datasource" maxrows="25" cachedwithin="#CreateTimeSpan(0,0,30,0)#">
SELECT distinct email
FROM users
WHERE email LIKE <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.search#%">
ORDER BY email
</cfquery>
<!--- Build result array --->
<cfloop query="data">
<cfset ArrayAppend(result, email)>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
maybe this helps
also make sure you have your cfform tags around your form, and make sure that your /cfide folder is mapped to your website.
looking at your code and comparing it... it may be the way your calling the cfc (filename)
try: autosuggest="cfc:getdata.get_data.({cfautosuggestvalue})"