Set session variable with select box via Ajax with Coldfusion - ajax

I'm trying to set a coldfusion session variable when the value of a select box changes. However I get an error on an alert box saying "Object HTMLSelectElement" and doesnt change the session variable. Any help would be great.
Code:
Form.cfm
<cfset session.item1 = 0>
<cfselect name="item1" class="form-control" id="item1">
<option value="0">0</option>
<option value="10">10</option>
<option value="20">20</option>
<option value="30">30</option>
<option value="40">40</option>
<option value="50">50</option>
</cfselect>
Script
$('#item1').change( function() {
var item1Selected = $('#item1').val();
$.ajax({
url: '\components.cfc?method=getItem1', // You repeated "url:" in your url
data: {item1: item1Selected}, // added missing final comma
success: function(item1Selected) { alert(item1); }
});
});
Components.cfc
<cffunction name="getItem1" access="remote" returntype="any">
<cfargument name="item1" type="any" required="yes">
<cfset session.item1 = #argument.item1#>
<cfreturn />
</cffunction>

Change this:
<cfset session.item1 = #argument.item1#>
to this:
<cfset session.item1 = arguments.item1>
The correct scope is arguments, the plural of argument. Plus, you don't need the pound signs.
Not related to your question, but if you get your CF code to work using CF only, this sort of troubleshooting becomes a lot easier.

(Too long for comments)
In addition to Dan's comments about the error in the CFFunction, two other issues that may prevent the code from behaving as you expect:
Your success() function defines the response variable as item1Selected. By using alert(item1) you are actually displaying the <select> element, not the response from the ajax call.
The cffunction does not actually return anything, ie <cfreturn /> So even if you fix the variable name, the response will be undefined or blank. Returning a response is not required. However, if you did want to return some sort of data to success() you need to modify the function to return something (JSON, etcetera).

Related

Coldfusion inserting db record and downloading file using Ajax

Im trying to create a button that when clicked inserts a record into the database and then downloads a file. I'm trying to do this via Ajax, the file download works but it isn't inserting the record in the db.
Trigger
<button type="button" class="btn btn-primary" onClick="downloadFile1()">Download</button>
Script
function downloadFile1(){
var file1 = 'https://example.com/files/myfile.zip';
$.ajax({
TYPE: "POST",
URL: "components.cfc?method=DownloadFile1",
data: {fileId : 3},
success: function() {
window.location = file1;
}
});
}
</script>
Components.cfc
<cfcomponent output="false">
<cffunction name="DownloadFile1" access="remote" returntype="string">
<cfcontent type="text/html">
<CFQUERY NAME="i">
INSERT into t_terms
(bid, fildid)
VALUES
(
<cfqueryparam value="#session.busid#" cfsqltype="CF_SQL_INTEGER">,
<cfqueryparam value="#url.fileID#" cfsqltype="CF_SQL_INTEGER">)
</CFQUERY>
</cffunction>
Could someone point out where I am going wrong?
Like Jack said above, since you're doing a POST and not a GET, instead of looking in the URL scope, try looking in the "FORM" scope.
<cfqueryparam value="#form.fileID#" cfsqltype="CF_SQL_INTEGER">
Or, you could just look for "fileID". If it is being passed in, your query will work. If not, it will throw an error.
<cfqueryparam value="#fileID#" cfsqltype="CF_SQL_INTEGER">

ColdFusion User login fails after session timeout

We’ve recently moved to cf11 for a project and have hit on an unusual problem:
When a user lets their session timeout, and they try to login back in it takes two attempts for them to successfully log in.
When a user manually logs out, they have no problems logging in.
This problem didn’t happen in CF8. I’ve examined user scopes and cannot see a difference. I’ve tried adding the logout code before sign-in in the hope I can make the state the same. Neither has worked. Is this a known problem? And do you have a suggestion to what I can try?
EDIT:
I have an Application.cfc and result.cfm in the root of the project.
I have a signin/ folder for pages that are not logged in. That contains signin.cfm and onsignin.cfm that handles the authentication.
When running the code, wait for the session to time out before logging in again with the same user name again.
signin/signin.cfm
<form action="onsignin.cfm" method="POST" >
User name: <input name="login" type="text" />
<input type="submit" value="Login" name="btnSubmit" >
</form>
signin/onsignin.cfm
<cffunction name="authenticate" access="public" returntype="void">
<cfargument name="login" />
<cflogin idletimeout="3600">
<cfloginuser name="#arguments.login#" password="1234AbCd" roles="admin,developer,login_session,login_signoff" />
</cflogin>
<cfreturn />
</cffunction>
<cfscript>
authenticate(trim(Login));
writeOutput("At /signin/onsignin.cfm<br/>");
writeOutput("IsUserLoggedIn: #IsUserLoggedIn()#<br /><hr/>");
</cfscript>
<cflocation url="../result.cfm" addToken="no" /><!--- before we get to result.cfm onRequestStart() in Application.cfc is triggered. --->
Application.cfc
<cfcomponent>
<cfset THIS.Name = "LoginTest" />
<cfset THIS.SessionManagement = true />
<cfset THIS.ClientManagement = false />
<cfset THIS.LoginStorage = "session" />
<cfset THIS.setClientCookies = false />
<cfset This.sessiontimeout= createTimeSpan(0,0,0,20)/><!--- 20second timeout to show the session problem --->
<cffunction name="outputCurrentLoginState" access="private">
<cfargument name="currentFunction" type="string" required="true"/>
<cfargument name="TargetPage" type="string" required="true"/>
<cfscript>
writeOutput("In function: #arguments.currentFunction# state is <br />");
writeOutput("TargetPage: #arguments.TargetPage# <br />");
writeOutput("GetAuthUser: #GetAuthUser()#<br />");
writeOutput("GetUserRoles: #GetUserRoles()#<br />");
writedump(session);
writedump(cookie);
writeOutput("IsUserLoggedIn: #IsUserLoggedIn()#<br /><hr />");
/*use writeLog() to view to Console */
</cfscript>
</cffunction>
<cffunction name="OnRequestStart" access="public" returntype="boolean">
<cfargument name="TargetPage" type="string" required="true"/>
<cfset outputCurrentLoginState("OnRequestStart",arguments.TargetPage)/>
<cfif not IsDefined("Cookie.CFID")>
<CFLOCK SCOPE="SESSION" TYPE="exclusive" TIMEOUT="5">
<cfcookie name="CFID" value="#SESSION.CFID#" secure="false" httpOnly="true" />
<cfcookie name="CFTOKEN" value="#SESSION.CFTOKEN#" secure="false" httpOnly="true" />
</CFLOCK>
</cfif>
<cfreturn true />
</cffunction>
<cffunction name="OnRequest" access="public">
<cfargument name="TargetPage" type="string" required="true"/>
<cfset outputCurrentLoginState("OnRequest",arguments.TargetPage)/>
<cfif findNoCase("signin/", arguments.TargetPage)>
<cflogout/>
</cfif>
<cfinclude template="#ARGUMENTS.TargetPage#" />
</cffunction>
</cfcomponent>
result.cfm
<cfscript>
writeOutput("GetAuthUser: #GetAuthUser()#<br />");
writeOutput("GetUserRoles: #GetUserRoles()#<br />");
writeOutput("--- IsUserLoggedIn: #IsUserLoggedIn()#<br />");
writeOutput("At /result.cfm<hr/>");
</cfscript>
Update: Now I have the test code above that fails for cf11, I tried it on a cf8 server and in cf8 it works as I expect it to. When the session times out, the user does not have any problem creating a new session. It is only in cf11 were it fails.
I have a work around that will be good enough for my use case.
Old code:
<cflogin idletimeout="3600">
<cfloginuser name="#yourlogin#" password="#yourpassword#" roles="#yourroles#" />
</cflogin>
New code:
<cflogin idletimeout="3600" allowconcurrent="false">
<cfloginuser name="#yourlogin##createUUID()#" password="#yourpassword#" roles="#yourroles#" />
</cflogin>
Adding a random value to the cfloginuser name attribute should make everyone unique, even if they use the same credentials when you validate them against a database.
It’s a bit of a hack, but it gets around the issue.
Thank you for everyone’s help… Especially Miguel for picking up on allowconcurrent.
I've added the following comment on 3839458:
Another workaround is duplicate the cflogin. Example:
<cflogin ..>
<cflogin ..>
When both have allowconcurrent=true (the default), then both will run. isUserLoggedIn() returns YES after the 1st, but the 1st login actually fails. The 2nd runs and logs in correctly.
Repro attached as Application.cfc
When THIS.loginStorage="cookie", then the issue does not reoccur if an old cfauthorization cookie is still present.
Thanks!,
-Aaron
Update
There is already a bug opened with Adobe about this issue - Bug 3839458. I suggest you add your experience and vote on that bug. It currently has a status of "ToTrack" with a reason of "PRNeedInfo". I have voted for it as well and added a reference to this question for more info.
Promoted from the comments
I think the problem is that you have set the idletimeout attribute of the <cflogin> tag to a value (3600 seconds) which is longer than your session timeout value (20 seconds). That is not a valid approach. I realize that you have set the session timeout at 20 seconds for ease of testing. However, it does not make sense to have a longer idletimeout than session timeout when you are setting your loginStorage to be session. The <cflogin> tag is attempting to keep the user's session active but the session itself is being torn down outside of it's control. That is asking for trouble in my opinion. Keep the idletimeout setting less than or at least equal to the session timeout setting.
I suspect the difference you are seeing between ColdFusion versions might have to do with a new attribute that was added to the <cflogin> tag in ColdFusion 11. The allowconcurrent attribute (documentation reference). By default that setting is set to true meaning concurrent login sessions should be allowed. Try setting that attribute to false, as in, <cflogin idletimeout="3600" allowconcurrent="false">. I believe that will make the <cflogin> tag work like it used to in ColdFusion 8.
You mention in the comments that setting allowconcurrent to false is not an acceptable solution. Why? That is how it is working for you on ColdFusion 8. I feel that the behavior you are seeing is "working as designed". However, if you feel that this is a bug then please enter a bug for it with Adobe.
As an aside, I also noticed that you are setting ClientManagement and setClientCookies both to false. Yet in your OnRequestStart method you are setting those cookies. Why? They are not needed for session management. The ColdFusion server will handle that for you.

<CFContent type=text/x-javascript> and Ajax

I am using Ajax on a CFC file. I can't get the result from the function that I am calling from Ajax. I set a debug message (Alert()), but can't see it. Any suggestion?
function getDelegateChartAcct(LocFund){
alert("#Arguments.PIUniversalID#");
alert($F("DelegateFund"));
new Ajax.Request( "?method=displayDelegateChartAcct",
{
parameters : {
PIUniversalID: "#Arguments.PIUniversalID#",
PILocFund: $F("DelegateFund")
},
method : "post"
}
);
}
<cffunction name="displayDelegateChartAcct" access="remote" output="true"
returntype="void">
<CFArgument name="PIUniversalID" type="string" required="true" />
<CFArgument name="LocFund" required="true" type="String" />
<CFSET var chartacctlist = runChartAcctDelegationQuery
(#Arguments.PIUniversalID#, #Arguments.LocFund#)>
<CFContent type="text/x-javascript">
alert(“Hi”);
// delegateChartAcctList();
// $("DelegateChartAcct").
// <CFOutput query="chartacctlist">
// $("DelegateChartAcct").insert( new Element(
"option", { value : "#JSStringFormat( chart_acct )#", selected :
// "selected" } ).update( "#JSStringFormat( chart_acct )#" ) );
// </CFOutput>
</cffunction>
Thanks,
Kefang
You are not returning anything from the CFC (and you can't run JavaScript inside the function you are calling remotely, either).
You have 2 ways you can handle this:
Load the form (or element) you want by making a remote call to a .cfm file that builds the form (or element) you want. You can use $("{place form will be displayed}").load("{url to .cfm page}) and jQuery will make an HTTP request adn load the result in the DOM element that matches the selector.
Change your CFC to return the query and populate the select box on the client side using JavsScript. (This is what I would do)
Your code would look like this:
<cffunction name="displayDelegateChartAcct" access="remote" output="true" returntype="query">
<cfargument name="PIUniversalID" type="string" required="true" />
<cafrgument name="LocFund" required="true" type="String" />
<cfset var chartacctlist = runChartAcctDelegationQuery (#Arguments.PIUniversalID#, #Arguments.LocFund#)>
<cfreturn chartacctlist />
</cfcomponent>
You could then use the following to load that data (looks like Prototype based on the syntax):
new Ajax.Request( "?method=displayDelegateChartAcct&returnFormat=json",
{
parameters : {
PIUniversalID: "#Arguments.PIUniversalID#",
PILocFund: $F("DelegateFund")
},
method : "post",
onSuccess: function(response) {
// code in here will populate select
}
}
);
The 'returnFormat=json' tells ColdFusion to return the results as JSON.
You would just need a JS handler to take that data and use it to populate the SELECT box.

How do I pass an argument to a CFC through AJAX?

I'm using the following scrip to call a CFC function:
function loadQuery() {
$.get('QueryData.cfc',{},function(GetMyData){
$("#content").html(GetMyData)
})
return false
}
$(document).ready(function() {
$("#loadLink").click(loadQuery)
});
This is my HTML:
Load It
<div id="content"></div>
I am calling the following CFC:
<cffunction name="GetMyData" access="public" returntype="query">
<cfargument name="RecordID" type="string" required="yes">
<cfset var RecordData = "">
<cfquery name="RecordData" datasource="MyDSN">
SELECT
foo.RecordID,
foo.RecordName
FROM
foo
WHERE
foo.RecordID = #ARGUMENTS.RecordID# ;
</cfquery>
<cfreturn RecordData>
Problem one is when I call the CFC, the CFC page shows up; the CFC description comes up (after asking for the Admin pass). I don't want to load QueryData.cfc; I want to execute the function inside QueryData.cfc.
The second issue is I can't figure out the syntax for passing an argument to the CFC method.
You can do something similar with the $.get method, but I usually do something like this:
$(document).ready(function() {
$("#loadLink").click(function(e) {
e.preventDefault();
var recordata = $(this).attr("href").substring(1); //trim '?' char
$.ajax({
type: "GET",
url: "QueryData.cfc?method=GetMyData",
data: recordata,
dataType: "html",
success: function(message) {
$("#content").html(message);
}
});
});
});
Where the data for the record ID is stored somewhere in the DOM like so:
Load Data
<div id="content"></div>
Also, not sure how it behaves with access="public" - it might still work - but it should probably be access="remote" on your function.
For what you're doing, would you like to try <cfdiv> or <cfajaxproxy>? It's much easier.
But to answer your question, the GET url should be XXX.cfc?method=whatever&param=xyz
edit: btw your function should have access="remote", and it's not a good idea to return Query object, unless you're using <cfgrid>.

Ajax call in ModelGlue ColdFusion Application without rendering a view

I am using Ajax with ModelGlue in a ColdFusion Application. I want to make an Ajax call to return a value. I do not want to render any view. I just want a database interaction and bring back a value.
My Ajax call:
new Ajax.Request(root+'test.testFunction',{
method: 'post',
parameters: {param1:paramval},
onSuccess: function(response){
alert(response.responseText);
var myresult = response.responseText;
}
});
my modelglue event :
<event-handler name="test.testFunction">
<broadcasts>
<message name="testFunction" />
</broadcasts>
</event-handler>
and my controller function:
<cffunction name="testFunction" returnType="any" output="true" >
<cfargument name="event" type="any" required="true">
<cfset justtest = 1>
<cfreturn justtest>
</cffunction>
I am using prototype as my ajax library.
When i alert the responseText i am getting null value. Is this bcoz i have not included the view part in the event handler? If i included the view part then i wud have to create a new page which i dont want to do. Is it possible to get just a server value by ajax call without rendering any view?
I want to have myresult value as 1 according to the above scenario.
Pls help. Thnx for any help.
When you say you "just want to bring back a value" -- that's your "view". What you want to do is use a special view for your remote (ajax) event that just spits out the value. For example, if you want it to return JSON, you might do this:
Event Configuration:
<event-handler name="test.testFunction">
<broadcasts>
<message name="testFunction" />
</broadcasts>
<views>
<include name="body" template="renderJson.cfm" />
</views>
</event-handler>
Controller function:
<cffunction name="testFunction" returnType="any" output="true" >
<cfargument name="event" type="any" required="true">
<cfset event.setValue('justtest', 1) />
</cffunction>
renderJson.cfm:
<cfoutput>#serializeJson(event.getValue('justtest'))#</cfoutput>
If you're using Model-Glue 3, you can use the new Event Formats feature to piggy-back this ajax view on an existing event that does the same thing for a different view-format.
Try using this at the end of your controller function:
<CFCONTENT TYPE="text" RESET="Yes"><CFOUTPUT>#serializeJSON(justTest)#
<cfset request.modelGlueSuppressDebugging = true />
<cfsetting showdebugoutput="false" /></CFOUTPUT><cfabort>
So like this:
<cffunction name="testFunction" returnType="any" output="true" >
<cfargument name="event" type="any" required="true">
<cfset justtest = 1>
<CFCONTENT TYPE="text" RESET="Yes"><CFOUTPUT>#serializeJSON(justTest)#
<cfset request.modelGlueSuppressDebugging = true />
<cfsetting showdebugoutput="false" /></CFOUTPUT><cfabort>
</cffunction>
This will keep your current view and return 'justTest' as a json.
If you are using firefox you should be able to see the reponse from the server.
WARNING: if Coldfusion 8, and have OnRequest() in application.cfc, there may be a related known cf8 bug. For a workaround, see
http://www.coldfusionjedi.com/index.cfm/2008/3/19/Ask-a-Jedi-Ajaxbound-requests-and-onRequest
In this situation, you should really be calling the remote proxy of your service, bypassing the MVC framework. :)
Oh, and don't forget you can use <cfajaxproxy> if you're using CF8.

Resources