I am running into a weird issue with my ColdFusion 10 code. I am new to ColdFusion, so go easy on me. The reason it is weird is because it does not seem to occur in older versions of this platform (i.e. MX 7).
A little info first:
I have two environments. A ColdFusion 10 and a ColdFusion MX 7 (IIS 7 and IIS 5, respectively). In the ColdFusion 10 environment, I have an Application.cfc file with the following statement...
<cfset CompanyLogoText = "Acme Company">
This Application.cfc file is in the web root (mydomain.com). I also have a CFM file in a sub folder of the web root at mydomain.com/pages/default.cfm. It contains the following markup...
<cfoutput><p>#CompanyLogoText#</p></cfoutput>
The issue
When I navigate to mydomain.com/pages/default.cfm, I get an error from coldfusion. The error is "Variable COMPANYLOGOTEXT is undefined."
The weird part
I am not getting this error in the ColdFusion MX 7. The only difference is that the CF MX 7 environment uses a Application.cfm file, but with the same exact line.
Question
How can I get the pages/default.cfm file to see my variable CompanyLogoText in the CF 10 environment?
Here is the full markup
Application.cfc
<cfcomponent>
<cfset This.name = "test_cf">
<cfset This.Sessionmanagement="yes">
<cfset This.Sessiontimeout="#createtimespan(0,0,10,0)#">
<cfset This.applicationtimeout="#createtimespan(5,0,0,0)#">
<cfset This.setclientcookies="no" >
<cfset This.clientmanagement="no">
<cffunction name="onApplicationStart">
<cfset CompanyLogoText = "Acme Company">
</cffunction>
<cffunction name="onRequestStart">
<cfargument name="requestname" required=true />
<cfset CompanyLogoText = "Acme Company">
</cffunction>
</cfcomponent>
Pages/Default.cfm
<cftry>
<cfoutput><p>#CompanyLogoText#</p></cfoutput>
<cfcatch>
<p>Could not read CompanyLogoText<br/><br/>
<cfoutput>
<br/>Message: #cfcatch.message#
<br/>Details: #cfcatch.detail#.
</cfoutput>
</cfcatch>
</cftry>
That's the difference between Application.cfm and Application.cfc
Use onRequest(), set the variables, then cfinclude the target file. That's the only way to share the variables scope.
https://wikidocs.adobe.com/wiki/display/coldfusionen/onRequest
e.g.
<cffunction name="onRequest" returnType="void">
<cfargument name="targetPage" type="String" required=true/>
<cfinclude template="globalVars.cfm">
<cfset variables.foo = "bar">
<cfinclude template="#Arguments.targetPage#">
</cffunction>
QUOTE: CF8: Migrating from Application.cfm to Application.cfc
Put in the onRequest method any code that sets Variables scope
variables and add a cfinclude tag that includes the page specified by
the method's Arguments.Targetpage variable.
As mentioned your application.cfc needs to be formatted correctly. Your best bet is to give this a read and format your .cfc accordingly.
http://www.bennadel.com/blog/726-ColdFusion-Application-cfc-Tutorial-And-Application-cfc-Reference.htm
Don't see an answer marked yet. If you have an application.cfm file in the sub-directory it will override the application.cfc in the root. Just a possibility ...
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've migrated a ColdFusion application from ColdFusion 10 to ColdFusion 2016. After Migration, Application variables are not staying in its scope, it is refreshing on each and every request.
Consider the following example,
Application.cfm
<cfsetting enablecfoutputonly="true" />
<CFAPPLICATION NAME="Test App"
SETCLIENTCOOKIES="YES"
CLIENTMANAGEMENT="YES"
SESSIONMANAGEMENT="YES"
SESSIONTIMEOUT="#CREATETIMESPAN(0,8,0,0)#"
APPLICATIONTIMEOUT="#CREATETIMESPAN(1,0,0,0)#">
<cfdump var="#Application#" label="app">
<CFLOCK SCOPE="APPLICATION" TYPE="EXCLUSIVE" TIMEOUT="10">
<CFSET Application.Email = "test#test.com">
<CFSET Application.DataSource="test">
</cflock>
Test.cfm
<CFLOCK SCOPE="APPLICATION" TYPE="READONLY" TIMEOUT="10">
<cfset Application.one = 1>
<cfset Application.two = 2>
<cfset Application.three = 3>
</cflock>
OnRequestEnd.cfm
<cfsetting showdebugoutput="false" />
<cfdump var="#Application#" label="onRequestEnd">
So if we request /test.cfm
it'll throw the following output
Again refreshing the page also giving the same output
Not sure why the Application scoped variables are losing its persistence.
the following is the expected output..
Any idea of Why the application variables are lost and getting refreshed on each and every request ?
I haven't tested this code, but what you're seeing is the procedural order of operation performed by Application.cfm. You're essentially redefining the application on every request, which is why on the name exists in your initial dump and the rest exist on the dump in onRequestEnd.
If you update your code to use Application.cfc, you can ditch the cflock code, better organize your "triggers" and define your application variables once, when needed, using onApplicationStart.
<cfcomponent>
<cfset this.name = "Test App">
<cfset this.SETCLIENTCOOKIES="YES">
<cfset this.CLIENTMANAGEMENT="YES">
<cfset this.SESSIONMANAGEMENT="YES">
<cfset this.SESSIONTIMEOUT="#CREATETIMESPAN(0,8,0,0)#">
<cfset this.APPLICATIONTIMEOUT="#CREATETIMESPAN(1,0,0,0)#">
<cfsetting enablecfoutputonly="true" />
<cffunction name="onApplicationStart" access="public" returnType="void" output="false">
<cfset application.Email = "test#test.com">
<cfset application.DataSource="test">
</cffunction>
<cffunction name="onRequestStart" access="public" returntype="boolean" output="false">
<cfset application.one = 1>
<cfset application.two = 2>
<cfset application.three = 3>
<cfreturn true>
</cffunction>
<cffunction name="onRequestEnd" access="public" returntype="boolean" output="false">
<cfsetting showdebugoutput="false" />
<cfdump var="#application#" label="onRequestEnd">
<cfreturn true>
</cffunction>
</cfcomponent>
This should define email and datasource in the application scope one time, when the app first loads. The variables one, two and three will be created at the start of each request, but you can add a check to set them only if they don't already exist.
You can then use child Application.cfc files to help modularize your application using sub-folders and sub-application specific variables. They'll still exist in the scope of the larger application, but you'll be able to manage them from a location specific to a sub-app this way.
Looking through the logs, we're getting hundreds of the following
"Error","jrpp-185","08/21/12","10:05:43","PATH","www.domain.com
Agent:Mozilla/4.0 (compatible; Synapse)
Error: An exception occurred when invoking a event handler method from Application.cfc.
The method name is: onRequest.
They seem to be mostly search bots. The on place on APplication.cfc that I can see reference to the function is below
<cffunction name="onRequest" returnType="void">
<cfargument name="targetPage" type="String" required=true/>
<cfsetting enablecfoutputonly="yes" requesttimeout="20">
<cfparam name="url.refresh" default="0">
<cfset request.strMember = Duplicate(session.strMember)/>
<cfset request.tNow = GetTickCount()>
<cfif url.refresh EQ 0>
<cfset request.iCacheHr = 12/>
<cfelse>
<cfset request.iCacheHr = 0/>
</cfif>
<cflogin>
<cfif IsDefined("session.strMember.sRoles")>
<cfloginuser name="#session.strMember.sFirstName##session.strMember.sLastName#"
password="12345"
roles="#session.strMember.sRoles#"/>
</cfif>
</cflogin>
<cfinclude template="core/incl/SessionLogger.cfm">
<cfinclude template="core/incl/LinkTranslator.cfm">
<cfinclude template="core/incl/udf.cfm">
<cfinclude template="urlcheck.cfm"/>
<cfinclude template="#Arguments.targetPage#">
</cffunction>
From that, can anyone please advise on what's wrong and how to fix it? I'm fairly new to CF and this is making me pull out what little hair I have left
1) You use two different coding styles
<cfparam name="url.refresh" default="0">
<cfset request.strMember = Duplicate(session.strMember)/>
Invalid/left open XML tags in first line and valid (closed) XML tags in the second line.
Try to stick to one (preferably the last one).
2) You are using old way of checking variable being defined
IsDefined("session.strMember.sRoles")
read about newer (and better and faster)
StructKeyExists(session.strMember, "sRoles")
3) Most likely your code is calling
<cfloginuser ... >
at every page request
4) Make sure that paths for all includes are correct and they themselves don't have any errors.
Simplify your method until you stop getting an error and then investigate what exactly is causing it
Are the bots hitting a page that doesn't exist?
Maybe try changing the last line to:
<cfif fileExists(expandPath(Arguments.targetPage))>
<cfinclude template="#Arguments.targetPage#">
<cfelse>
<cfabort>
</cfif>
Maybe you could detect if they are a bot and server them something else? depends on how search friendly you want your site to be:
http://www.bennadel.com/blog/1083-ColdFusion-Session-Management-And-Spiders-Bots.htm
I am trying to convert an application I support from application.cfm to application.cfc. I followed Ben Nadel's ColdFusion Application.cfc Tutorial And Application.cfc Reference, but my pages cannot access any of the variables in the APPLICATION scope, without adding APPLICATION. to the variable. As a side note, this application uses 0 cfc's currently.
here is my application.cfc.
<cfcomponent displayname="Application" hint="Handle the application" output="true">
<cfset THIS.Name = "AAS" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 60, 0 , 0) />
<cfset THIS.SessionManagement = true />
<cfset THIS.setClientCookies = false />
<cfset THIS.versionNum = '1'>
<cfset THIS.genericUserID = 'o005265'>
<cfset THIS.genericPassword = 'zo005265'>
<cffunction
name="OnApplicationStart"
hint="Fires when the application is first created."
access="public"
output="false"
returntype="boolean">
<cfset APPLICATION.aasURL = 'http://127.0.0.1:8500/aaas'>
<cfset APPLICATION.dsn = 've0_aaas'>
<cfset APPLICATION.aas_system = 'development (studio)'>
<cfreturn true />
</cffunction>
</cfcomponent>
Basically I just copied what was in the application.cfm page, and figured it would work the same. I am guessing that I have to invoke this somewhere? That is the only thing that I can think of. Any help would be great.
--- EDIT ---
I have added the OnRequest and OnApplicationStart methods after #EvikJames answer
<cffunction name="OnApplicationStart" access="public" returntype="boolean" output="false" hint="Fires when the application is first created.">
<!--- Return out. --->
<cfset APPLICATION.aasURL = 'http://127.0.0.1:8500/aaas'>
<cfset APPLICATION.datasource = 've0_aaas'>
<cfset APPLICATION.aas_system = 'development (studio)'>
<cfreturn true />
</cffunction>
<cffunction name="OnRequest" access="public" returntype="void" output="true" hint="Fires after pre page processing is complete.">
<!--- Define arguments. --->
<cfargument name="TargetPage" type="string" required="true">
<!--- Include the requested page. --->
<cfinclude template="#ARGUMENTS.TargetPage#" />
<cfset VARIABLES.dsn = APPLICATION.dsn>
<cfset VARIABLES.aasURL = APPLICATION.aasURL>
<cfset VARIABLES.aas_system = APPLICATION.aas_system>
<!--- Return out. --->
<cfreturn />
</cffunction>
You aren't really trying to use "application" variables (which always need to be scoped). I suspect your old application.cfm page had something like.
<cfapplicatin name="blah"...>
<cfset dsn = 'mydsn'/>
And then you were able to do:
<cfquery datasource="#dsn#">
This approach does not utilze the application scope ... it is merely taking advantage of the fact that your application.cfm always runs no matter what. What it is actually doing is putting variables in the variables scope. Because CF always checks the "variables" scope first you soemthing like #dsn# works - but that is not the same as the application scope.
To mimic this behavior in Application.cfc (as has been suggested) you will need to put your variable in the "onRequest()" function instead of the "onApplicationstart()" function - like so:
<cffunction name="onRequest">
<cfset dsn = 'mydsn'/>
</cffunction>
That is expected. To reference application variables you need to prefix them with application.
In your onApplication start method, do this:
<cfset APPLICATION.datasource = 'MyDSN'>
In your onRequest method, do this:
<cfset VARIABLES.DSN = APPLICATION.datasource>
Then, this query will work:
<cfquery datasource="#dsn#">
// sql
</cfquery>
I should add that when you are fully upgraded, you can remove all of the code above just set the default datasource:
<cfset THIS.datasource = 'MyDSN'>
If the variable is in the application scope, you will always need to identify it that way in your .cfm pages. If you use a variable name without the scope prefix, the application scope is not looked at.
if you want to declare variables in the application.cfc that can be accessed without the application scope in your other pages, simply declare the variables outside of any functions.
<component>
<cfset this.name = "applicationName">
<cfset otherVar = 'something'>
<cfset otherVar2 = 'something else'>
<cffunction name="onApplicationStart>.....</cffunction>
</component>
otherVar and otherVar2 can be called without scope prefix on all .cfm pages.
It sounds like you were not originally using application scoped variables. If the variables were not originally scope with "application." then they were simply in "variables scope" (confusing wording I know) which is accessible by the cfm page hit and others included. That is one big change when moving between application.cfm and application.cfc. The general idea there follows the principle that included CFM files share variables scope and CFC files do not.
If the only change you have to make is changing #dsn# to #appplication.dsn# then just do it and get it over with. There are tools such as WinGrep or even Notepad++ which have find and replace across multiple files.
2 Questions -
In CF8 in the application.cfm I cold set a global variable
like so
<cfset DSN = "dej6_42">
I am now trying to adjust to the Application.cfc in CF10/Lucee and can not figure out how to set this same variable.
Here is my current Application.cfc
<cfcomponent output="false">
<cfset This.name = "My Application">
<cfset This.clientmanagement="True">
<cfset This.loginstorage="Session">
<cfset This.sessionmanagement="True">
<cfset This.sessiontimeout="#createtimespan(0,0,10,0)#">
<cfset This.applicationtimeout="#createtimespan(5,0,0,0)#">
<cfset DSN = "dej6_42">
</cfcomponent>
I have tried
<cfset This.DSN = "dej6_42">
Then tried to call in a separate page
<cfoutput>#Applicaton.DSN#</cfoutput>
I think from my research I will need to use both application.cfc and application.cfm to accomplish the above. *edit - I tried to add an include at the end of the application.cfc file to applciation.cfm and it did not work.
2 Question.
When I place any of the standard functions in the Application.cfc my site turns to a blank page
Here is that Application.cfc - I if I remove everything below the DSN set then it will display the site.
<cfcomponent output="false">
<cfset This.name = "My Application">
<cfset This.clientmanagement="True">
<cfset This.loginstorage="Session">
<cfset This.sessionmanagement="True">
<cfset This.sessiontimeout="#createtimespan(0,0,10,0)#">
<cfset This.applicationtimeout="#createtimespan(5,0,0,0)#">
<cfset DSN = "dej6_42">
<cffunction name="onApplicationStart">
</cffunction>
<cffunction name="onApplicationEnd">
</cffunction>
<cffunction name="onRequestStart">
</cffunction>
<cffunction name="onRequest">
</cffunction>
<cffunction name="onRequestEnd">
</cffunction>
<cffunction name="onSessionStart">
</cffunction>
<cffunction name="onSessionEnd">
</cffunction>
<cffunction name="onError">
</cffunction>
</cfcomponent>
Your example doesn't set a global variable. It sets a variable in the variables scope: it will not be accessible to any CFC-based code nor any custom tags used within the request. It'll only be available in the Application.cfm, the file requested, files it includes, and OnRequestEnd.cfm
Application.cfc is a CFC (to state the obvious), so variables-scoped variables set within it are only available within it. If you want to set an application-wide variable, you need to put it in the application scope. Application scope variables should be set in the onApplicationStart) handler which is run once when the application starts, but not on every request. By way of comparison Application.cfm (which is misnamed) is run on every request. It should be called OnRequestStart.cfm.
So to be clear, setting an application-scoped variable in onApplicationStart would be as thus:
function onApplicationStart() {
application.DSN = "dej6_42";
}
If you use an onRequest() interceptor, and within that include the originally requested file, then the request will be run in the context of the Application.cfc instance, and variables set within onRequest will be available to the rest of the mainline request code, much like the way you set your variable in Application.cfm. Semantically though, if you mean a variable to exist for the life of the application (like a DSN), then putting it in the application scope is the best bet.
It sounds to me from the inferences one can make from your question that your app architecture might be languishing in the 1990s. I think you should read up on using a framework (eg: FW/1 or ColdBox) to better organise your code in a maintainable and scalable way.
Also you should read up on Application.cfc (and method reference). And probably CFCs in general: Using ColdFusion components-Developing guide.
You also might want to think about modernising your approach to writing CFML and spare the tags for view code, and otherwise using script. It makes the code easier to follow for both you and other developers who might end up needing to maintain it if the whole app isn't cluttered up with tags.
You need to set it into the application scope
<cfcomponent output="false">
<cfset This.name = "My Application">
<cfset This.clientmanagement="True">
<cfset This.loginstorage="Session">
<cfset This.sessionmanagement="True">
<cfset This.sessiontimeout="#createtimespan(0,0,10,0)#">
<cfset This.applicationtimeout="#createtimespan(5,0,0,0)#">
<cfset application.DSN = 'dej6_42'>
</cfcomponent>