Binding Data to a CFTEXTAREA - coldfusion

I am populating a cfselect box using the bind attribute. I'd like to be able to make my selection from the two drop downs and then populate a CFTEXTAREA based on that selection. I thought I could just issue a similar bind attribute command but the textarea outputs:
[object Object]
CF8 documentation says that this is possible? What am I missing?
Form Code:
<cfselect name="descriptionDD" value="description" bind="cfc:cfcs.menudata.getData()" bindonload="true" />
<cftextarea name="detailDD" value="service_detail" bind="cfc:cfcs.menudata.getDetail({descriptionDD})" bindonload="true" />
CFC Code:
<cfcomponent>
<!---First Select Box --->
<cffunction name="getData" access="remote" returntype="query">
<!--- Function to get data from datasource --->
<cfquery name="data" datasource="ezpay">
select description
from service_descriptions
order by description
</cfquery>
<!--- Return results --->
<cfreturn data>
</cffunction>
<!---Second Select Box --->
<cffunction name="getDetail" access="remote" returnType="query">
<cfargument name="description" type="any" required="true">
<cfquery name="dataDetail" datasource="ezpay">
SELECT service_detail
from service_descriptions
WHERE description = '#ARGUMENTS.description#'
ORDER BY ID ASC
</cfquery>
<cfreturn dataDetail>
</cffunction>
</cfcomponent>

but the textarea outputs: [object Object]
That usually means you are returning a complex object where a simple string is expected instead. [object Object] is essentially the string representation of the complex object (in your case a query).
TextArea's are designed to display a single value. So your bind function should return a string, not a query object.
<cffunction name="getDetail" access="remote" returnType="string">
<cfargument name="description" type="any" required="true">
<!--- localize function variables --->
<cfset var dataDetail = "">
<cfquery name="dataDetail" datasource="ezpay">
SELECT service_detail
FROM service_descriptions
<!--- adjust cfsqltype if needed --->
WHERE description = <cfqueryparam valu="#ARGUMENTS.description#" cfsqltype="cf_sql_varchar">
ORDER BY ID ASC
</cfquery>
<cfreturn dataDetail.service_detail>
</cffunction>

Related

Coldfusion autosuggest not working for suggestions in the middle of the string

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"

using autocomplete with commas in a field

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>

How to secure a webservice CFC for use with AJAX

I have a CFC file with remote access enabled, I am using this to perform various admin tasks such as adding / removing image records from a database. The calls to this CFC are made through AJAX via custom JavaScript on my admin pages. I placed the CFC in what I thought was a secured directory but having had some issues with images disappearing of their own accord and I have since found it is not secure at all.
I would like to secure the CFC. There is already a session based security CFC that I use for the admin pages, and the protect method of that gets called each time one of the admin pages is requested, this redirects a user if the authentication fails. Can I use something this on my CFC? IF so, what is the best way to implement it? If not, how should I implement security on it?
Below is my example CFC:
<cfcomponent
name="test"
displayname="test"
output="false"
hint="test"
>
<!--- pseudo constructor --->
<cfscript>
variables.propertyImageDAO = CreateObject("component","cfcs.dataobjects.property_imageDAO").init(APPLICATION.dsn);
variables.propertyImageGateway = CreateObject("component","cfcs.dataobjects.property_imageGateway").init(APPLICATION.dsn);
</cfscript>
<!--- constructor --->
<cffunction name="init" access="public" output="false" returntype="any"
hint="Constructor for this CFC">
<!--- return this CFC --->
<cfreturn this />
</cffunction>
<!--- CRUD methods (create, read, update, delete) --->
<!--- CREATE: inserts a new property_image into the database --->
<cffunction name="createRecord" access="remote" output="true"
hint="Creates a new property_image record and returns a struct containing a boolean (success) indicating the success or
failure of the operation, an id (id), and a string (message) containing a message"
>
<cfargument name="name" type="any" required="false" default="" />
<cfargument name="alt" type="any" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
var propertyImageBean = CreateObject("component","cfcs.beans.property_image").init(
'',
arguments.name,
arguments.alt
);
results = propertyImageDAO.createRecord(propertyImageBean);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- READ: reads a property_image from the database and populates the property_image object --->
<cffunction name="readRecord" access="remote" output="true" returntype="void"
hint="Reads property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="id" type="numeric" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
propertyImageBean = CreateObject("component","cfcs.beans.property_image");
propertyImageBean.setid(arguments.id);
propertyImageDAO.readRecord(propertyImageBean);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(propertyImageBean)#</cfoutput>
</cffunction>
<!--- DELETE: reads a property_image from the database and populates the property_image object --->
<cffunction name="deleteRecord" access="remote" output="true" returntype="void"
hint="Reads property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="id" type="numeric" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
results = propertyImageDAO.deleteRecordById(arguments.id);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- DELETERECORDS: deletes a property_image from the database --->
<cffunction name="deleteRecords" access="remote" output="true" returntype="void"
hint="Deletes property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="imageIdList" type="string" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- delete DB records --->
<cfscript>
results = propertyImageDAO.deleteRecordsByIdList(arguments.imageIdList);
</cfscript>
<!--- delete files --->
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- DELETERECORDS: reads a property_image from the database and populates the property_image object --->
<cffunction name="deleteRecordById" access="remote" output="true" returntype="void"
hint="Deletes property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="id" type="numeric" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- delete DB records --->
<cfscript>
results = propertyImageDAO.deleteRecordById(arguments.id);
</cfscript>
<!--- delete files --->
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- DELETERECORDSBYIDLIST: reads a property_image from the database and populates the property_image object --->
<cffunction name="deleteRecordsByIdList" access="remote" output="true" returntype="void"
hint="Deletes property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="imageIdList" type="string" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- delete DB records --->
<cfscript>
results = propertyImageDAO.deleteRecordsByIdList(arguments.imageIdList);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<cffunction name="deleteImagesByNameList" access="remote" output="true" returntype="void"
hint="Deletes property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="imageNameList" type="string" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- delete DB records --->
<cfscript>
results = propertyImageDAO.deleteImagesByNameList(arguments.imageNameList);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- READ: reads a property_image from the database and populates the property_image object --->
<cffunction name="getByIdList" access="remote" output="true" returntype="void"
hint="Reads property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="imageIdList" type="string" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
qGetByIdList = propertyImageGateway.getByIdList(arguments.imageIdList);
</cfscript>
<!--- convert into JSON friendly format --->
<cfif qGetByIdList.recordCount GT 0>
<cfset images = ArrayNew(1)>
<cfloop query="qGetByIdList" startRow="1" endRow="#qGetByIdList.recordCount#">
<cfscript>
// create image struct and assign values
image = StructNew();
image.id = id;
image.name = name;
image.alt = alt;
// append to JSON response
ArrayAppend(images,image);
</cfscript>
</cfloop>
<cfset results.images = images>
</cfif>
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- READ: reads a property_image from the database and populates the property_image object --->
<cffunction name="updateRecord" access="remote" output="true" returntype="void"
hint="Reads property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="id" type="numeric" required="true" />
<cfargument name="name" type="any" required="true" />
<cfargument name="alt" type="any" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
propertyImageBean = CreateObject("component","cfcs.beans.property_image").init(
arguments.id,
arguments.name,
arguments.alt
);
results = propertyImageDAO.updateRecord(propertyImageBean);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
To enforce your authentication logic, you should wrap all remote CFC calls in Application.cfc with this logic.
Unfortunately you're using CF8, so you can't use the onCFCRequest method of Application.cfc to easily wrap all your remote requests. But you can do the same thing in onRequestStart by checking if the target page ends in '.cfc'.
<cffunction name="onRequestStart">
<cfargment name="targetPage">
<cfif right(targetPage, 4) eq '.cfc'>
<!--- Perform authentication check --->
<cfif not loggedIn>
<!--- Return "unauthorized" to the client --->
<cfheader statuscode="401">
<cfabort>
</cfif>
</cfif>
</cffunction>
Then, in your Ajax fail handler, check for a 401 status code and display a message to the user indicating the need for logging in.
Why don't you just use a session token every time you make ajax call.
Place your authentication logic (validate session) into a remote facade, or if you are using any MVC framework, put the authentication logic onto the controller layer.
Return the appropriate HTTP status code (e.g. 403) if session validation fails so the frontend code can react appropriately.

ColdFusion autosuggest in text field is not responsive

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})"

returning multiple stored procedure result sets from a cfc

I am trying to convert some pages from my app to use cfc's, and one page uses a stored procedure to retrieve a couple sets of data.
Now when I access the results, they act just like a if I used a <cfquery> tag, and all of the functionality that gives. So now I am trying to use this same stored procedure in a cfc that I am building, and I would like to be able access the results in the same manner, and there in lies my problem. I'm not sure how to return multiple queries from the function, without creating an array, which I have started. By the way, the function is incomplete. I was just trying to get something to work. In the below setup I get an array of query objects, but I feel there is a better way to do it.
Here is the <cffuntion>:
<cffunction name="getProfileData"
access="public"
output="false"
returntype="string">
<cfargument name="cusip" type="string" required="true">
<cfargument name="report_date" type="date" required="true">
<cfset var errorMessage = "everything is good">
<cftry>
<cfstoredproc datasource="#dsn#" procedure="prc_asset_profile_retrieve">
<cfprocparam type="in" cfsqltype="cf_sql_varchar" value="#cusip#" dbvarname="#cusip">
<cfprocparam type="in" cfsqltype="cf_sql_varchar" value="#report_date#" dbvarname="#reportDate">
<cfprocresult name="profile_head" resultset="1">
<cfprocresult name="attribution" resultset="2">
<cfprocresult name="characteristics" resultset="3">
<cfprocresult name="exposure" resultset="4">
<cfprocresult name="weights" resultset="5">
<cfprocresult name="holdings" resultset="6">
</cfstoredproc>
<cfset var profileArray = []>
<cfset #ArrayAppend(profileArray,profile_head)#>
<cfcatch type="any">
<cfset errorMessage = "something happened">
</cfcatch>
</cftry>
<cfreturn profileArray>
</cffunction>
When I output some test data, it matches up
<cfset count = fund_profile.getProfileData("#cusip#","#report_date#")>
<cfdump var="#count[1]#">
<cfoutput>
From cfc (##count[1].recordCount##): #count[1].recordCount#<br>
From stored proc (##profile_head.recordCount##): #profile_head.recordCount#
</cfoutput>
I get:
From cfc (#count[1].recordCount#): 1
From stored proc (#profile_head.recordCount#): 1
But the second way looks so much cleaner.
-----------------------------WORKING SOLUTION------------------------------
So after working with the answer from #leigh, I came up with this.
Here is the full cfc:
<cfcomponent displayname="Fund Profile" hint="This is the cfc that will do the processing of all fund profile information" output="false">
<cfproperty name = "result1"> <!--- PROFILE HEAD --->
<cfproperty name = "result2"> <!--- ATTRIBUTION --->
<cfproperty name = "result3"> <!--- CHARACTERISTICS --->
<cfproperty name = "result4"> <!--- EXPOSURE --->
<cfproperty name = "result5"> <!--- WEIGHTS --->
<cfproperty name = "result6"> <!--- HOLDINGS --->
<cffunction name="init"
displayname="init"
hint="This will initialize the object"
access="public"
output="false"
returnType="Any">
<cfargument name="dsn" type="string" required="true" />
<cfargument name="cusip" type="string" required="true" />
<cfargument name="report_date" type="date" required="true" />
<cfset variables.dsn = #arguments.dsn#>
<cfset variables.cusip = #arguments.cusip#>
<cfset variables.report_date = #arguments.report_date#>
<cfscript>
getProfiledata(cusip,report_date);
</cfscript>
<cfreturn this>
</cffunction>
<cffunction name="getProfileData"
access="private"
output="false"
returntype="void">
<cfargument name="cusip" type="string" required="true">
<cfargument name="report_date" type="date" required="true">
<cfstoredproc datasource="#dsn#" procedure="prc_asset_profile_retrieve">
<!--- STORED PROCEDURE HASN'T CHANGED. SEE ABOVE FOR CODE --->
</cfstoredproc>
<cfscript>
setProfilehead(profile_head);
setAttribution(attribution);
setCharacteristics(characteristics);
setExposure(exposure);
setWeights(weights);
setHoldings(holdings);
</cfscript>
<cfreturn>
</cffunction>
<!--- NOT GOING TO INCLUDE ALL SETTERS AND GETTERS, --->
<!--- BECAUSE THEY ARE ALL THE SAME OTHER THAN THE NAMES --->
<cffunction name="setProfileHead" access="private">
<cfargument name="ProfileHead">
<cfset variables.result1 = arguments.ProfileHead>
</cffunction>
<cffunction name="getProfileHead" access="public" returntype="query">
<cfreturn variables.result1>
</cffunction>
</cfcomponent>
Here is the code from the calling page:
<cfset fund_profile = CreateObject("component", "CFCs.fund_profile").init("#dsn#","#cusip#","#report_date#")>
<cfset profile_head = fund_profile.getProfileHead()>
Sorry for all the code, but I wanted to make the code available. So does anyone see any problems with what I came up with?
A function can only return a single value. If you wish to return multiple values, you will need to use some type of complex object (an array, structure, ...) If arrays are not intuitive enough, you could place the queries in a structure and return that instead. Then the calling page could access the queries by name, rather than index.
(Side note, be sure to properly var scope/localize all function variables.)
<cfset var data = {}>
...
<!--- store query results in structure --->
<cfset data.profile_head = profile_head>
<cfset data.attribution = attribution>
...
<cfset data.holdings = holdings>
<!--- return structure --->
<cfreturn data>
I would create other methods in the CFC that would each be responsible for returning a result from the stored proc. In the main method , call setters
setProfileHead(profilehead:profileHead)
<cffunction name=ProfileHead>
<cfarguments name=ProfileHead />
<cfset variables.profilehead = arguments.profilehead>
</cffunction>
Then...
<cffunction name=GetProfileHead>
<cfreturn variables.profileHead />
</cffuction>