I have read many posts by people who have problems with onSessionEnd. This is my first conversion of application.cfm to application.cfc and the onSessionEnd is not working with the CFFunction I am trying to invoke.
I guess what's hanging this up is how to properly call the component from the /lib/components/ folder where it resides.
When a user logs in I am creating a session array that tracks a jobNumber and the last_completed_step in that job. There are multiple jobs in a users session. At the end of the session I want to write the updated array data back to the DB.
I should make it clear that at present I look into my log file and see that the session is started - as coded in the onSessionStart shown below. Furthermore, the onSessionEnd also writes to the log file when I take out the invocation of the component. In other words if I just tell it to write "Session ended." to the log file I will see it in the log file. I have set current session timeout in CF Administrator and my app.cfc for 3 minutes for testing.
If I call the "giveMeAnswer" method in the jobState.cfc from a separate file (also at the root level) the giveMeAnswer method works properly and returns the value "I am a CFC."
If I move the jobState.cfc to the root level and set the component attribute to "jobState" I am also getting a return from the component.
<!--- Runs when your session starts --->
<cffunction name="onSessionStart" returnType="void" output="false">
<!--- :: invoke all session variables | moved out of on session start :: --->
<cfinvoke component="#application.virtualPaths.cfcPath#system/sessionVars" method="init" />
<cflog file="#This.Name#" type="Information" text="Session started.">
</cffunction>
<!--- Runs when session times out --->
<cffunction name="onSessionEnd" returntype="void">
<cfargument name="SessionScope" type="struct" required="true" />
<cfargument name="ApplicationScope" type="struct" required="true" />
<cfinvoke component="/lib/components/jobState" method="giveMeAnswer" returnvariable="returnFromCfc">
</cfinvoke>
<cflog file="#This.Name#" type="Information" text="Session ended. #returnFromCfc#">
<cfreturn />
</cffunction>
So, is it just not finding the component? Any other ideas?
Thanks much, Jerry
I know I've seen folks use / in component calls before, but I do not believe it is officially supported. You want to use a dot notation path instead, ala
component="lib.components.jobstate"
and assure that lib is either a subdirectory or a known CF mapping that points to the lib folder.
Related
I recently installed ColdFusion 2018 at work and have been frustrated by the inability to get scope working correctly. As usual I put all my .cfcs into the /CFC folder, and none of them will execute without a blank application.cfm file in that folder. I have tried extending application, including application, proxy extending application, moving the CFCs to the root folder only gets me syntax error on JSON. I have read every article that I can find for the past two weeks and I am still unable to understand why scope will not work. It seems I can set session variables within the /CFC folder, but they are not available outside the folder? I have not worked with CF for a few years, but consider myself versed, and for the life of me cannot get this working. its probable that I am missing the forest due to trees, but if anyone would be willing to assist, I would be grateful.
instantiated object;
application.SessionMgr = CreateObject(this.obj,'CFC.SessionMgr').init('session');
proxy call;
cfajaxproxy cfc="CFC/SessionMgr" jsclassname="SessionMgr";
return is correct;
var s = new SessionMgr();
var setReport = s.setValue('ReportID', document.getElementById('cboReportKey').value);
alert(setReport);
however even manually setting session.ReportID = 7 will not persist outside the folder.
here is the SessionMgr.init
this is the init;
<cffunction name="init" access="public" returntype="SessionMgr" output="no" hint="I instantiate and return this object.">
<cfargument name="scope" type="string" required="yes">
<cfargument name="requestvar" type="string" default="SessionInfo">
<cfset var scopes = "application,Client,Session">
<cfif Not ListFindNoCase(scopes, arguments.scope)>
<cfthrow message="The scope argument for SessionMgr must be a valid scope (#scopes#)." type="MethodErr">
</cfif>
<cfset variables.scope = arguments.scope>
<cfset variables.requestvar = arguments.requestvar>
<cfset updateRequestVar()>
<cfreturn this>
</cffunction>
and the setValue fn
<cffunction name="setValue" access="remote" hint="I set the value of the given user-specific variable." returntype="string">
<cfargument name="variablename" type="string" required="yes">
<cfargument name="value" type="any" required="yes">
<cfset var val = arguments.value />
<cfset SetVariable("#arguments.variablename#", val) />
<cfset r = Evaluate(arguments.variablename) />
<cfreturn r />
</cffunction>
ok, after trying everything, heres the solution. extending by proxy doesnt work for this situation, tried that. What finally worked was creating an application.cfc IN the /CFC folder and stripping out all functional components from the /root application.cfc and simply ensuring the application name was the same in the stripped down version in /CFC folder as the /root cfc name. This apparently psuedo extends all the functionality from the /root application.cfc and makes everything available to the framework in the /CFC folder. Thanks to everyone here helping to get me to think outside my wheelhouse and resolving this issue.
We have multiple applications running on the same server and default log files end up being a mess of everything, especially the Exception log for which the admin panel does not offer search capacities.
Is it at all possible to have Coldfusion log things pertaining to a given application (as defined by Application.cfm or .cfc) to a separate log?
If not, any alternative solutions to this issue?
I'd recommend application.cfc onError method to log uncaught errors in separate log file. Also this doc could be helpful: Handling errors in Application.cfc
Here's a basic example of using the onError function in application.cfc to log errors to an application-specific log file.
<cfcomponent output="false">
<cfset this.name = "myApp">
<cffunction name="onError">
<cfargument name="exception" required="true">
<cfargument name="eventName" type="string" required="true">
<cfset var myAppName = this.name>
<cfset var details = "">
<!--- You need to specify and check for the exception details you want to include in your log text --->
<cfif IsDefined( "exception.type" )>
<cfset details = "Type: #exception.type#. ">
</cfif>
<cfif IsDefined( "exception.message" )>
<cfset details = details & "Details: #exception.message#">
</cfif>
<cflog type="error" file="#myAppName#" text="#details#">
<!--- Specify how you want the error to be handled once it's been logged --->
<cfdump var="#exception#">
</cffunction>
</cfcomponent>
You need to specify which parts of the exception details you want to include in your log entry, bearing in mind that the keys of the exception struct will vary according to the type of error thrown. It's therefore best to check for their existence before adding them to the log text.
Official onError docs for ColdFusion MX7 (since your question's tagged with that version - which is also why I've used tags rather than cfscript in my example to be on the safe side).
I have a website with multiple tabs. Each tab runs a separate report based on a set of filters that take their values from session variables.
How things work now:
While the user is inside a report tab they can open a filter menu to select the options that they need to run their report (doctor names, locations, date, etc) and then they can hit the run button to get their report. When the user clicks "run" the form is saving the variables inside the session where they are available to run other reports without having to click "run" or define them again and again.
What I am trying to do:
Instead of having only a "run" button inside the form I need an "Apply" button that will set the session variables from the form without running the current report. This way the user can pre-define their variables without being forced to run a report they don't need.
I tried using ajax that calls a function outside my application which is setting up variables based on the user's selection.
My challenge is to get those variables back from the function in some format where I could use them in updating the current session variables.
This is a sample of my code:
The Apply button:
Apply
My Ajax Function:
function setSession(){
var formData = $('form').serialize();
$.ajax({
url:'/mod_example/components/exampleCFCs/xUtility.cfc?method=setSessionVariables',
data: formData
});
};
And part of my function:
<cfcomponent output="no">
<cffunction name="setSessionVariables" access="remote" returntype="any">
<cfargument name="docid" type="string" required="no">
<cfif isDefined('docid')>
<cfset session.doctorids = docid>
</cfif>
<cfif isDefined('docid')>
<cfreturn session.doctorids>
<cfelse>
<cfreturn 0>
</cfif>
</cffunction>
</cfcomponent>
What I need is to get the value of session.doctorids to be able to update my session variables with the new value.
It sounds like you have this utility cfc in a shared directory and you are calling it directly. As you've noticed, the problem with that is that you end up with multiple sessions. You can get around this issue be setting up a Facade cfc within your application and make your ajax calls to that cfc.
If you only want to expose the setSessionVariables then you could use this cfc:
<cfcomponent output="no">
<cffunction name="setSessionVariables" access="remote" returntype="any">
<cfset var xUtility = createObject('component','mod_example.components.exampleCFCs.xUtility')>
<cfreturn xUtility.setSessionVariables(argumentCollection=ARGUMENTS)>
</cffunction>
</cfcomponent>
If you want to expose all methods of the utility cfc, then you can extend it:
<cfcomponent output="no" extends="mod_example.components.exampleCFCs.xUtility">
</cfcomponent>
This would allow you to call methods on the utility cfc while maintaining a single session scope (per user of course).
EDIT:
Been a while since i've worked in wheels...but i remember not liking AJAX in the wheels framework. If you create a new subfolder and call it 'remoting' and put the facade in there, and drop an application.cfc in there that looks like this:
<cfcomponent >
<cfset this.name = 'whatever_your_wheels_app_name_is'>
<cfset this.SessionManagement=true>
</cfcomponent>
You should be able to use that facade and this application.cfc will piggyback on the existing application with the same name. The problem with this approach would be if the application times out, and a remote call is the first request to the application, then the wheels application scope might not get set up properly.
It would be best if you could extend the root application.cfc and just override the onRequestStart method so that the framework will ignore the request. To do that you would need to make a mapping in the cfadmin to the root of your project and use this for your remoting/application.cfc
<cfcomponent extends="mappingName.Application">
<cffunction name="onRequestStart">
<cfargument name="requestname" required="true" />
<cfset structDelete(this,'onRequest')>
<cfset structDelete(this,'onRequestEnd')>
<cfset structDelete(VARIABLES,'onRequest')>
<cfset structDelete(VARIABLES,'onRequestEnd')>
<cfreturn true>
</cffunction>
</cfcomponent>
The way that wheels uses `cfinclude' all over the place, you may need to look at this post about extending the appliciation: http://techblog.troyweb.com/index.php/2011/09/cfwheels-workarounds-numero-uno-application-proxy/
There are some wheels plugins (http://cfwheels.org/docs/1-1/chapter/wheels-ajax-and-you) that allow you to use the controller actions / views / routes via ajax so you could look into those also.
I am new to Mura and have a lot of existing code that I am trying to utilize. I have a globalFunction.cfc file that has a lot of functions that I need to have access to for the existing code. Previously I always extended my application.cfc to the global function so they where always there. With Mura I am not sure where to include it and still keep the installation "upgrade safe".
Any suggestions are appreciated.
In your [site]/includes folder is an Application.cfc. I believe that is the one you are looking to have extend your globalFunction.cfc. It is update safe.
Lance,
You can just put any functions you're wanting to use throughout your site in your eventHandler or contentRenderer files in your theme's folder. These are update safe, and depending on how you're wanting to use them, you can use one for display and the other for function.
EventHandler Ex:
<!--- PAGE - Default --->
<cffunction name="onPageDefaultBodyRender" output="true" returntype="any">
<cfargument name="$">
<cfif $.getcontentID() neq "00000000000000000000000000000000001">#$.dspInclude('/themes/MYTHEME/display_objects/bodies/dsp_body_default.cfm')#</cfif>
</cffunction>
ContentRenderer Ex:
<cffunction name="removeLinks" returntype="string" access="public">
<cfargument name="str" default="" required="true">
<cfset str=reReplace(str, "<[[:space:]]*[aA].*?>(.*?)<[[:space:]]*/[[:space:]]*a[[:space:]]*>","\1","all") />
<cfreturn trim(str) />
</cffunction>
The EventHandler here just puts out an different body if its on the home page, where the contentRenderer removes any links if i use $.removeLinks(MYURLSTRING).
HTH
I'm trying to see if there is a way to split a CFSAVECONTENT tag across the onRequestStart() and onRequestEnd() functions in Application.cfc to save the generated HTML of any .cfm page in the application to a variable.
Adding <cfsavecontent variable="html"> to onRequestStart() and adding </cfsavecontent> to onRequestEnd() isn't allowed since the tag must be closed in the function.
Is this even possible to do? I'm trying to avoid hard coding the CFSAVECONTENT this into every .cfm page of the site.
Thanks!
Alex,
You could do something like this in OnRequest (untested, but should work).
<cffunction name="onRequest" returnType="void">
<cfargument name="thePage" type="string" required="true">
<cfsavecontent variable="html">
<cfinclude template="#arguments.thePage#">
</cfsavecontent>
<!--- do whatever you want with the html variable here (for example, output it) --->
<cfoutput>#html#</cfoutput>
</cffunction>
I realize this has an accepted answer already, but another way to accomplish this without using cfinclude would be to use the getPageContext() object in onRequestEnd() to nab the generated content:
<cffunction name="onRequestEnd" output="yes">
<cfargument type="string" name="targetPage" required="true" />
<cfset var html = getPageContext().getOut().getString() />
<!--- Manipulate the html variable. --->
<cfoutput>#html#</cfoutput><cfabort />
</cffunction>
The <cfabort /> is important here because if you don't abort the request, the CF engine will output the generated content again and it will end up sending two copies of the output along.
I've used this method to apply site-wide changes to content on sites in a crunch where finding every instance of the original content wasn't practical or timely enough. It can also be used to send the generated content out to a translation service if needed before being returned to the end-user.