Coldfusion and Related Selects - coldfusion

For several years, I have used dropdowns to determine the requirements for a SQL query and have never needed to use dropdowns with ‘related selects’ until now. I found a sample on how to do this and I can create related selects (with static data) which works well, see below using my data relating to vehicles and makes.
However, when I come to apply this to my environment, and use dynamic SQL query data, I cannot get it to work. I am confident that all the pieces of code are working together (as it works perfectly for the static data), but if I try and build the data automatically and then create the string to pass to the queryNew function, it fails.
This is the working Content.cfc file with STATIC query strings/data array
<cfcomponent>
<cfset tblMake = queryNew("name,id", "varchar,varchar", [{name:'RENAULT',id:'RENAULT'},{name:'RENAULT',id:'RENAULT'},{name:'RENAULT',id:'RENAULT'}])>
<cfset tblModel = queryNew("name,code,continent_id", "varchar,varchar,varchar", [{name:"TSERIES",code:"TSERIES",continent_id:"RENAULT"},{name:"MASTER",code:"MASTER",continent_id:"RENAULT"}])>
<cfset tblVoltage = queryNew("name,code", "varchar,varchar", [{name:"24 volt",code:"MASTER"},{name:"12 volt",code:"TSERIES"}])>
<cffunction name="getContent" access="remote" returntype="query" output="true">
<cfargument name="strTableName" type="string" required="true">
<cfargument name="strID" type="string" required="true">
<cfargument name="strName" type="string" required="true">
<cfargument name="intDistinct" type="numeric" required="false" default="0">
<cfargument name="selectedCol" type="string" required="false" default="0">
<cfargument name="selectedID" type="string" required="false" default="0">
<cfquery name="qryContent" dbtype="query">
select
<cfif arguments.intDistinct eq 1>distinct</cfif>
#arguments.strID# as theID,
#arguments.strName# as theValue
from #arguments.strTableName#
<cfif arguments.selectedID neq 0>
where #arguments.selectedCol# = '#arguments.selectedID#'
</cfif>
order by #arguments.strName#
</cfquery>
<cfreturn qryContent />
</cffunction>
</cfcomponent>
When I try and create the query using this method, nothing appears to work. I am looping through the database (from a query) and then creating a string using the required structure and the passing it to the function.
<cfcomponent>
<cfset XSTATIC = ""/>
<cfquery name="NOXVehicleModels" datasource="EBSNOX" >
SELECT DISTINCT VehicleMake
FROM [dbo].[NOX-Master]
WHERE VehicleMake IS NOT NULL
</cfquery>
<cfloop query="NOXVehicleModels">
<cfset XSTATIC = XSTATIC & "{name:'" & #Trim(NOXVehicleModels.VehicleMake)# & "',id:'"& #Trim(NOXVehicleModels.VehicleMake)# & "'},"/>
</cfloop>
<cfset XLEN=LEN(#XSTATIC#)/>
<cfset XSTATIC = MID(XSTATIC,1,XLEN-1)/>
<cfoutput>#XSTATIC#</cfoutput>
<cfset tblMake = queryNew("name,id", "varchar,varchar", [#XSTATIC#])>
I have created a separate cfoutput to test the string for structure etc and it appears to be correct, but it is just not passing to the querynew function. This is what the output looks like:-
{name:'CUMMINS',id:'CUMMINS'},{name:'DAF',id:'DAF'},{name:'IVECO',id:'IVECO'},{name:'MAN',id:'MAN'},{name:'MERCEDES',id:'MERCEDES'},{name:'RENAULT',id:'RENAULT'},{name:'SCANIA',id:'SCANIA'},{name:'VOLVO',id:'VOLVO'}
{name:'RENAULT',id:'RENAULT'},{name:'RENAULT',id:'RENAULT'},{name:'RENAULT',id:'RENAULT'}
What I have done so far:-
I have checked that the quotes are not important i.e. single/double.
I have tried to build the query outside of the tag.
Again, all appears (?) to be in order and I cannot understand that could be the problem. Possibly the structure is missing something before parsed to the function?
Any help would be greatly appreciated.
Thanks,
Jack

Related

Convert Special Characters to HTML - ColdFusion

I need to convert a lot of special characters to their html format and I am trying to do this with a function that is using ReplaceList but something is wrong with the function or the values I am passing to it.
This is the function
<cffunction name="HtmlUnEditFormat" access="public" returntype="string" output="no" displayname="HtmlUnEditFormat" hint="Undo escaped characters">
<cfargument name="str" type="string" required="Yes" />
<cfscript>
var lEntities = "&##xE7;,&##xF4;,&##xE2;,Î,Ç,È,Ó,Ê,&OElig,Â,«,»,À,É,≤,ý,χ,∑,′,ÿ,∼,β,⌈,ñ,ß,„,´,·,–,ς,®,†,⊕,õ,η,⌉,ó,­,>,φ,∠,‏,α,∩,↓,υ,ℑ,³,ρ,é,¹,<,¢,¸,π,⊃,÷,ƒ,¿,ê, ,∅,∀, ,γ,¡,ø,¬,à,ð,ℵ,º,ψ,⊗,δ,ö,°,≅,ª,‹,♣,â,ò,ï,♦,æ,∧,◊,è,¾,&,⊄,ν,“,∈,ç,ˆ,©,á,§,—,ë,κ,∉,⌊,≥,ì,↔,∗,ô,∞,¦,∫,¯,½,¤,≈,λ,⁄,‘,…,œ,£,♥,−,ã,ε,∇,∃,ä,μ,¼, ,≡,•,←,«,‾,∨,€,µ,≠,∪,å,ι,í,⊥,¶,→,»,û,ο,‚,ϑ,∋,∂,”,℘,‰,²,σ,⋅,š,¥,ξ,±,ℜ,þ,〉,ù,√,‍,∴,↑,×, ,θ,⌋,⊂,⊇,ü,’,ζ,™,î,ϖ,‌,〈,˜,ú,¨,∝,ϒ,ω,↵,τ,⊆,›,∏,",‎,♠";
var lEntitiesChars = "ç,ô,â,Î,Ç,È,Ó,Ê,Œ,Â,«,»,À,É,?,ý,?,?,?,Ÿ,?,?,?,ñ,ß,„,´,·,–,?,®,‡,?,õ,?,?,ó,­,>,?,?,?,?,?,?,?,?,³,?,é,¹,<,¢,¸,?,?,÷,ƒ,¿,ê,?,?,?,?,?,¡,ø,¬,à,ð,?,º,?,?,?,ö,°,?,ª,‹,?,â,ò,ï,?,æ,?,?,è,¾,&,?,?,“,?,ç,ˆ,©,á,§,—,ë,?,?,?,?,ì,?,?,ô,?,¦,?,¯,½,¤,?,?,?,‘,…,œ,£,?,?,ã,?,?,?,ä,?,¼, ,?,•,?,«,?,?,€,µ,?,?,å,?,í,?,¶,?,»,û,?,‚,?,?,?,”,?,‰,²,?,?,š,¥,?,±,?,þ,?,ù,?,?,?,?,×,?,?,?,?,?,ü,’,?,™,î,?,?,?,˜,ú,¨,?,?,?,?,?,?,›,?,"",?,?";
</cfscript>
<cfreturn ReplaceList(arguments.str, lEntities, lEntitiesChars) />
</cffunction>
This is how I am calling it:
<cfoutput>
<cfloop query="local.q" startrow="2">
#HtmlUnEditFormat(consultServiceType)# <br />
</cfloop>
</cfoutput>
These are the strings I am passing to it:
Security?
Security Guard®
Alarm System©
Private Investigator;
I am not getting any errors back (I had a cftry in the function before) and the strings come back the same
EDIT:
I've tried using #FindNoCase('©',consultServiceType)# and is returning 0 so I guess something is wrong with the string I am passing in?
You're using CF11, did you try EncodeForHTML() ?
The accepted answer is the better approach (don't reinvent the wheel), but your function isn't working because you have lEntities and lEntitiesChars mixed up.
<cffunction name="HtmlUnEditFormat" access="public" returntype="string" output="no" displayname="HtmlUnEditFormat" hint="Undo escaped characters">
<cfargument name="str" type="string" required="Yes" />
<cfscript>
var lEntities = "&##xE7;,&##xF4;,&##xE2;,Î,Ç,È,Ó,Ê,&OElig,Â,«,»,À,É,≤,ý,χ,∑,′,ÿ,∼,β,⌈,ñ,ß,„,´,·,–,ς,®,†,⊕,õ,η,⌉,ó,­,>,φ,∠,‏,α,∩,↓,υ,ℑ,³,ρ,é,¹,<,¢,¸,π,⊃,÷,ƒ,¿,ê, ,∅,∀, ,γ,¡,ø,¬,à,ð,ℵ,º,ψ,⊗,δ,ö,°,≅,ª,‹,♣,â,ò,ï,♦,æ,∧,◊,è,¾,&,⊄,ν,“,∈,ç,ˆ,©,á,§,—,ë,κ,∉,⌊,≥,ì,↔,∗,ô,∞,¦,∫,¯,½,¤,≈,λ,⁄,‘,…,œ,£,♥,−,ã,ε,∇,∃,ä,μ,¼, ,≡,•,←,«,‾,∨,€,µ,≠,∪,å,ι,í,⊥,¶,→,»,û,ο,‚,ϑ,∋,∂,”,℘,‰,²,σ,⋅,š,¥,ξ,±,ℜ,þ,〉,ù,√,‍,∴,↑,×, ,θ,⌋,⊂,⊇,ü,’,ζ,™,î,ϖ,‌,〈,˜,ú,¨,∝,ϒ,ω,↵,τ,⊆,›,∏,",‎,♠";
var lEntitiesChars = "ç,ô,â,Î,Ç,È,Ó,Ê,Œ,Â,«,»,À,É,?,ý,?,?,?,Ÿ,?,?,?,ñ,ß,„,´,·,–,?,®,‡,?,õ,?,?,ó,­,>,?,?,?,?,?,?,?,?,³,?,é,¹,<,¢,¸,?,?,÷,ƒ,¿,ê,?,?,?,?,?,¡,ø,¬,à,ð,?,º,?,?,?,ö,°,?,ª,‹,?,â,ò,ï,?,æ,?,?,è,¾,&,?,?,“,?,ç,ˆ,©,á,§,—,ë,?,?,?,?,ì,?,?,ô,?,¦,?,¯,½,¤,?,?,?,‘,…,œ,£,?,?,ã,?,?,?,ä,?,¼, ,?,•,?,«,?,?,€,µ,?,?,å,?,í,?,¶,?,»,û,?,‚,?,?,?,”,?,‰,²,?,?,š,¥,?,±,?,þ,?,ù,?,?,?,?,×,?,?,?,?,?,ü,’,?,™,î,?,?,?,˜,ú,¨,?,?,?,?,?,?,›,?,"",?,?";
</cfscript>
<cfreturn ReplaceList(arguments.str, lEntitiesChars, lEntities) />
</cffunction>
<cfoutput>#htmluneditformat("Company?")#</cfoutput>
Further, #ReplaceList()# in both ACF and Railo/Lucee recurse through the list, which means the order of the lists matter. With the fix I suggest, ? becomes &le;. A fix to this would be to move & and the code for it to the beginning of each list.
Consider this simple piece of code
<cfoutput>#replacelist("abc","a,b","b,c")#</cfoutput>
You would probably expect the output to be "bcc", but that's not how ReplaceList works, it works something more like this
<cfset sx = "abc">
<cfset listf = "a,b">
<cfset listr = "b,c">
<cfloop from="1" to="#listlen(listf)#" index="i">
<cfset sx = replace(sx,listgetat(listf,i),listgetat(listr,i),"ALL")>
<!--- iteration one replaces a with b to make bbc --->
<!--- iteration two replaces b with c to make ccc --->
</cfloop>
I'm not suggesting that someone use this code when CF has the built in functionality, I'm merely explaining why it doesn't work and a pitfall of ReplaceList().

How to run a cfquery within the onRequestStart() function?

I want to run a query on every page that a user requests. This query is needed to get back preferences set by the user's organisation for the application. This is what I have tried:
<cffunction name="onRequestStart" access="public" returntype="boolean">
<cfargument type="String" name="TargetPage" required="true"/>
<cfquery name="rsSettings">
SELECT *
FROM
dbo.Settings
</cfquery>
<cfreturn true>
</cffunction>
</component>
However each pages that looks for the rsSettings recordset says that its not defined. If I put the same query within each page that needs it then it works fine.
Does onRequestStart() not handle cfquery?
<cfquery name="request.rsSettings">
SELECT *
FROM
dbo.Settings
</cfquery>
Then in the page use:
request.rsSettings.columName

How to display <cfreturn> output from a CFC being called from a form

I have a web form which uses the action attribute to call a CFC like this:
<form action="mycfc.cfc?method=registeruser">
The CFC processes the data in the form and then I want it to return a variable telling the user if their form submission has been successful or not.
So within my CFC, I put this:
<cffunction name="registeruser" access="remote" hint="registers a new user" returnformat="JSON">
... PROCESSES FORM HERE THEN...
<cfset Msg = 'Success'>
<cfreturn Msg>
<cflocation url = "/registrationpage.cfm">
</cffunction>
How do I display the Msg variable in the registrationpage.cfm page? My output is set to JSON so I guess I have to DeSerialize but I have no idea how to actually reference/access this output from the method.
My whole answer is for educationnal purposes only and I strongly advise you to use an existing framework rather than reinventing the Wheels. Have a look at Picking a ColdFusion MVC Framework
You can store the value in the session scope. A lot of frameworks does it using a flash memory concept, which is a temporary memory (implemented as a struct) that destroys members when accessed.
Have a look at http://cfwheels.org/docs/1-1/chapter/using-the-flash it's quite straight forward to implement an API that does this.
Client code could look like (depending on your implementation):
<cfset session.flash.set('importantMsg', 'some important msg')>
<cflocation ...>
Then from the other page:
<cfif session.flash.has('importantMsg')>
<!--- The following line should also destroy the 'importantMsg' key --->
#session.flash.get('importantMsg')#
</cfif>
Here's an implementation example (not that the implementation is not thread-safe):
FlashMemory.cfc
<cfcomponent>
<cffunction name="init" returntype="FlashMemory">
<cfset variables.instance = {flash = {}}>
</cffunction>
<cffunction name="set" returntype="void">
<cfargument name="key" type="string" required="true">
<cfargument name="value" type="any" required="true">
<cfset variables.instance.flash[arguments.key] = arguments.value>
</cffunction>
<cffunction name="get" returntype="any">
<cfargument name="key" type="string" required="true">
<cfset var v = variables.instance.flash[arguments.key]>
<cfset structDelete(variables.instance.flash, arguments.key)>
<cfreturn v>
</cffunction>
<cffunction name="has" returntype="boolean">
<cfargument name="key" type="string" required="true">
<cfreturn structKeyExists(variables.instance.flash, arguments.key)>
</cffunction>
</cfcomponent>
onSessionStart
<cfset session.flash = new FlashMemory()>
Also please note that in your case, your remote CFC methods shouldn't return anything. You will use the flash memory to pass data around instead. That means when the method has finished it's work you can simply redirect the client.
You probably shouldn't use remote CFC methods in this particular case:
I have never really used remote CFC methods as stateful web services. The various advantages of remote CFC methods like their ability to spit out data in multiple data-interchange formats (JSON, WDDX...) is lost with your implementation.
You could simply do something like:
registration.cfm
<cfset errors = session.flash.get('registrationErrors')>
<cfif arrayLen(errors)>
<!--- Display errors --->
</cfif>
<form method="post" action="register.cfm">
...
</form>
register.cfm
<cfset registration = new Registration(argumentcollection = form)>
<cfset validator = new RegistrationValidator(registration)>
<cfif validator.validate()>
<cfset application.userService.register(registration)>
<!--- You can also redirect to a page that represents the correct state --->
<cflocation url="registered.cfm" addtoken="no">
<cfelse>
<!--- Store the errors collection in the flash memory --->
<cfset session.flash.set('registrationErrors', validator.errors())>
<!--- Redirect to the page the user came from --->
<cflocation url="#cgi.http_referer#" addtoken="no">
</cfif>
Take the cflocation tag out of your function. It will not execute anyway because it's after the cfreturn tag.
Also, post your form to a .cfm page, not the .cfc. From that .cfm page, do this:
<cfset MyObject = CreateObject(stuff for your cfc goes here)>
<cfset MessageFromFunction = MyObject.registeruser()>
<cfoutput>#MessageFromFunction</cfoutput.

Access function arguments from another function

I am trying to create a custom debug tool and I need to use a component with two separate functions in it. The first function (startTimer) has some arguments such as startCount and the other one (endTimer) has endCount. What I am trying to accomplish is something like the following code:
<cffunction name="startTimer" access="public" returntype="void">
<cfargument name="startActionTime" type="string" required="no">
</cffunction>
<cffunction name="endTimer" returntype="void" access="public">
<cfargument name="endActionTime" type="string" required="no">
<cfset finalTime = endActionTime - startTimer.startActionTime>
<!---Some SQL will go here to record the data in a db table --->
</cffunction>
And this is how I am calling the function
<cfscript>
location = CreateObject("component","timer");
loc =location.startTimer(
startActionTime = getTickCount()
);
end = location.endTimer(
endActionTime = getTickCount()
);
</cfscript>
I guess I am having scope issues because when I am trying to run the code I am getting an undefined error on startTimer.startActionTime. What is the correct way to do something like this?
You can use the variables scope like so:
<cfcomponent>
<cfset variables.startActionTime = 0>
<cffunction name="startTimer" access="public" returntype="void">
<cfargument name="startActionTime" type="numeric" required="no">
<cfset variables.startActionTime = arguments.startActionTime>
</cffunction>
<cffunction name="endTimer" returntype="string" access="public">
<cfargument name="endActionTime" type="numeric" required="no">
<cfset finalTime = endActionTime - variables.startActionTime>
<!---Some SQL will go here to record the data in a db table --->
<Cfreturn finaltime>
</cffunction>
</cfcomponent>
From the Adobe Docs: Variables scope variables created in a CFC are available only to the component and its functions, and not to the page that instantiates the component or calls its functions.

How to pass a struct to Coldfusion CFC using CFINVOKE?

I have a CFC file which handles all of the emails I'm sending form an application (using Coldfusion8).
I was using CFINVOKE to call the respective function inside this CFC and passed a struct with all user data along like so:
<cfscript>
var User.data = {};
User.data.name = "John Doe";
User.data.email = "john#doe.com";
...
</cfscript>
// call mailer
<cfinvoke component="mailer_user" method="say_hi">
<cfinvokeargument name="userData" value="#User.data#">
</cfinvoke>
And inside my mailer.cfc
<cffunction name="say_hi" access="public" output="false">
<cfargument name="userData" type="struct" required="true" />
....
For some reason this now fails and I can only get it to work if I pass fields seperately as cfargument, which is a a pain, since I'm passing a lot of data.
Question:
How can I get this to work using argumentCollection.
Even if I CFINVOKE like this:
<cfinvoke component="mailer_user" argumentcollection="#User.data#" method="say_hi"></cfinvoke>
it still doesn't do a thing. I'm setting output flags right before the cfinvoke and after, as well as inside the "say_hi" function going in and out. I'm only getting the flag before CFINVOKE.
Side note: This is all done through AJAX and I'm only getting back success="false" if my CFC has an error somewhere. I only work remotely on the system, so I can't set AJAX debugging in CFADMIN
As I typed the comment above it occurred to me what the problem is likely to be.
You are passing in a structure to your function. You pass User.data which has name,email,blah,etc as keys in that structure. Those keys need to match the arguments in your function
<cffunction name="say_hi" access="public" output="false">
<cfargument name="name" type="struct" required="true" />
<cfargument name="email" type="struct" required="true" />
<cfargument name="blah" type="struct" required="true" />
<cfargument name="etc" type="struct" required="true" />
If you want to pass in the structure as a argument, you would need to have a user.userData as your structure of user data and your function should be
<cffunction name="say_hi" access="public" output="false">
<cfargument name="userData" type="struct" required="true" />
When you pass the collection as argumentCollection you should do argumentCollection="#user#", so that the userData part matches your cfargument in the function.
Clear as mud?
I think you should stay in cfscript style by writing
// call mailer
mailUser = createObject("component", "mailer_user"); // or new mailer_user(); for CF9+
mailUser.say_hi(User.data);
That should work, if it doesn't, it's somewhere else in your code. Try looking at the error log.
You should map the variable to the data you pass, then no problem sending a struct. Do it this way
<cfset objMailer = createObject("component","mailer_user") />
<cfset objMailer.say_hi(userData:user.data)/>
This works even in CF7.
Ok. There was a typo inside my mailer CFC, where I had a variable with "##". As is was inside my email text
it went unnoticed...
So you can pass a struct allright using this:
<cfinvoke component="mailer_user" method="say_hi">
<cfinvokeargument name="userData" value="#User.userdata#">
</cfinvoke>
and grab it inside your called function like so:
<cffunction name="say_hi" access="public" output="false" hint="">
<cfargument name="userData" type="struct" required="true" hint="user data passed" />
<cfscript>
var internalInfo = "";
var User = {};
User.userdata = userData;
</cfscript>
...
Maybe someone else can use the snippet.