ColdFusion Application.cfm cfinclude not working - coldfusion

I have a function called "conv" which is needed in several programs within my application. So rather than include it in each program, I put it in the Application.cfm, which looked like this:
<cfapplication name = "Moxware" sessionmanagement = "Yes">
<cfset lang = "LU">
<cfset x127 = Chr(127)>
<cfset mmox = 'Moxware'>
<cfinclude template="conv.cfc">
When I ran one of the programs using the function "conv" (which is in conv.cfc) I got an error that the function conv could not be found.
After I hunted around the internet for ideas I tried this:
<cfapplication name = "Moxware" sessionmanagement = "Yes">
<cfset lang = "LU">
<cfset x127 = Chr(127)>
<cfset mmox = 'Moxware'>
<cffunction name="onRequestStart" output="true" returntype="void">
<cfinclude template="conv.cfc">
</cffunction>
That gave me the same error message as before.
Can someone explain to me how to do this?
Note that the function conv was tested and works just fine.

Instead of using an include, like this...
<cfinclude template="conv.cfc">
Try creating an object, like this...
<cfscript>
MyObject = createObject("component", "conv");
</cfscript>
When you want to access a function within that object, try this...
<cfscript>
SomeValue = MyObject.MyFunction();
</cfscript>

You can include CFM pages into the CFC, but I don't think you can include CFC code into a CFC.
So this is possible...
<cffunction name="OnRequest" access="public" returntype="void" output="true" hint="Fires after pre page processing is complete.">
<cfargument name="TargetPage" type="string" required="true" />
<cfinclude template = "/myMapping/onRequestStart_include.cfm" />
<cfinclude template = "/myMapping/onRequest_include.cfm" />
<cfinclude template = "/myMapping/onRequestEnd_include.cfm" />
</cffunction>
Note the use of a mapping (in our case /myMapping), which can help if your doing this in CFC's. If no mapping is needed, just drop that.
But probably the best option is to instantiate your CFC from within the Application.cfc and use it.
<cfset myConv = createObject("component", "myMapping.conv").init() />
Again using a mapping to get to the CFC. The .init() is not always needed, depends how your CFC is setup.
Then presumably conv has methods you want to use (You talk about it as a single function? A cfc is essentially an object, so you create it as an object first and then use it's methods), so then you'd invoke then by using...
myConv.functionName()
Put another way - Application.cfc is no different from anywhere else in your code. How ever it's invoked and used elsewhere is how you should do it here. The only trick might be pathing to that CFC, which you can do by creating a custom mapping.
Also consider going old school (if it is just a function), and using custom-tags.
Good luck.

Related

Scope not working outside of the CFC folder for Session scope

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.

Unable to access variables when converting from application.cfm to application.cfc

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.

What is the proper way to assign a general udf to application.cfc?

I simply want to define a function in application.cfc and expose it application wide to all requests. Preferably the "assignment" would only happen on application startup.
Is the preferred method to do something along the lines of this:
<CFCOMPONENT OUTPUT="FALSE">
<CFSET this.name = "Website">
<CFSET this.clientManagement = true>
<CFSET this.SessionManagement = true>
<CFFUNCTION NAME="GetProperty" OUTPUT="False">
<CFARGUMENT NAME="Property">
<CFRETURN this.Props[Property]>
</CFFUNCTION>
<CFFUNCTION NAME="OnApplicationStart" OUTPUT="FALSE">
<CFSET Application.GetProperty = GetProperty>
.
.
.
or is there something better?
By default, GetProperty will be visible in Variables scope already, this can be sufficient for many usages (in .cfm templates).
If you want to use these methods directly in the components, referencing them in the Application scope is fine.
Though I do this with Request scope in the onRequestStart(), it's just my personal preference. Something like this:
request.udf = {};
request.udf.halt = halt;
Please note that best practice in general is incapsulating the objects and having them referenced in variables scope of the host object. I typically do this when initializing the object, simply pass previously created objects as init() arguments.
P.S. Nowadays it is recommended to use lower case for tags and their attributes. Kind of good coding practices.
The best way to store site specific config data is probably going to be to create a new component named something such as SiteConfig.cfc with methods such as getProperty(propertyName) and setProperty(propertyName, value). You would then store this CFC in the application scope by doing the following inside Application.cfc's onApplicationStart method like:
<cfset application.siteConfig = createObject("component", "SiteConfig").init() />
Back to your original question though about storing a UDF in the Application scope, below is a way to do that. The basis is that in onApplicationStart you will create a new application persisted struct with your site's config properties like siteName and whatever else. Then a function is stored in a CFM file which is cfincluded only in onApplicationStart, then copied into the application scope. This means that all your regular page CFM files can use application.getProperty(propertyName).
Since the function is only created once and stored in the application scope it satisfies your original question's requirements about "assignment would only happen on application startup".
Hope this helps a bit!
getProperty.function.cfm
<cffunction name="getProperty" output="false">
<cfargument name="propertyName" type="string" required="true" />
<cfreturn application.config[propertyName] />
</cffunction>
Application.cfc
<cffunction name="onApplicationStart" output="false">
<cfset application.config = structNew() />
<cfset application.config.siteName = "My App's Display Name" />
<cfinclude template="getProperty.function.cfm" />
<cfset application.getProperty = variables.getProperty />
</cffunction>
test.cfm
<cfset propertyValue = application.getProperty("siteName") />
<cfdump var="#propertyValue#" />
You might consider creating a seperate "properties" CFC and instanciating it as
a singleton in the SERVER scope then it will be available from any CFML page even
if it isn't part of an application. If you go this route then there is no "server
start" event to bind to. Instead you can put this in the contructor of application.cfc
or in the body of application.cfm
<cfif not structkeyexists(server,"properties")>
<cflock name ="loadProperties"
timeout ="10"
type ="exclusive"
>
<cfif not structkeyexists(server,"properties")>
<cfset server.properties =
createObject("component","path-to-props.cfc")
.init({..inital properties..})
>
</cfif>
</cflock>
</cfif>
The lock code is to prevent the overhead of creating and assigning the UDF on every
request. This also allows the properties instance to persist so that having a
properties.SetProperty() function will work
you might also want to use the technique discussed here

Testing for existence of FORM scope / struct in ColdFusion

Problem: When requesting the WSDL for a CFC, I get the following error: Variable FORM is undefined. It happens in this line of code, in the OnRequestStart method in application.cfc
<cfif structKeyExists(form,'resetappvars')>
<cfset OnApplicationStart() />
</cfif>
If I request a specific method, it works fine. I have considered using cfparam to create a default form struct if none exists, but that seems like an ugly hack and I worry it will actually create the form struct in the variables or this scope of the CFC. Maybe this is a legitimate bug as well?
Note: This only happens when I request the WSDL, if I invoke a method directly - the code executes as expected without problems.
Update: Application.cfc code sample - just add any CFC to your app and request it with ?wsdl to see the issue. This has been tested (and failed) on ColdFusion 7 and ColdFusion 8.
<cfcomponent output="false">
<cffunction name="OnApplicationStart" access="public" returntype="boolean" output="false" hint="Fires when the application is first created.">
<cfset application.dsn = "my_dsn" />
<cfreturn true />
</cffunction>
<cffunction name="OnRequestStart" access="public" returntype="boolean" output="false" hint="Fires at first part of page processing.">
<cfargument name="TargetPage" type="string" required="true" />
<cfif structKeyExists(form,'resetappvars')>
<cfset OnApplicationStart() />
</cfif>
<cfreturn true />
</cffunction>
</cfcomponent>
Maybe try adding a:
<cfif IsDefined("form")>...</cfif>
around the above code?
You could also cfparam the variable you're looking for then just change your logic a little (assuming resetAppVars is a boolean:
<cfparam name="form.resetAppVars" default="false" />
...
<cfif form.resetAppVars>
<cfset OnApplicationStart() />
</cfif>
Edit: I'm not sure if the above code could be considered a hack, but it seems pretty standard CF, to me.
This post of Ben Nadel gives detailed list of scopes available for different types of requests.
By reading it you can easily find out that form scope is not available in given context, but url is.
I've heard it's just a matter of opinion, but it seems to me that it is improper to reference your form scope within a CFC, as there is no guarantee that the form scope will be available when your cfc is invoked and when your method is called. It is better to ensure that any data that needs to be available to the method is provided explicitly to your object. This can be done either by including an argument:
<cfargument name="resetAppVars" type="boolean" required="false" default="false" />
Then you check arguments.resetAppVars, and it is always defined, but defaulted to false.
Or by creating an attribute on your object and creating an explicit set method:
(at the top of your cfc)
<cfset this.resetAppVars = false />
<cffunction name="setResetAppVars" access="public" returnType="void" output="false">
<cfargument name="flagValue" type="boolean" required="true" />
<cfset this.resetAppVars = arguments.flagValue />
</cffunction>
In which case you will check against this.resetAppVars. You can also scope this locally using <cfset var resetAppVars = false /> as the declaration, which makes it a private attribute of your object, and is probably proper, so code that invokes the object cannot improperly overwrite this variable with a non-boolean type. In that case, you would simply refer directly to resetAppvars in your test, instead of using this scope.
You could also do this:
<cfif NOT isSoapRequest()>...
and stick your remaining logic inside that chunk.

Global Variables in ColdFusion

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>