extend Application.cfc, but not from the root - coldfusion

I have:
1. inetpub/wwwroot/ProjectName/Application.cfc
2. inetpub/wwwroot/ProjectName/Admin/Application.cfc
I want #2 to extend #1 and override the onRequest function.
I've looked into Sean Corfields's ApplicationProxy.cfc solution, but that is if your project is in the root folder, which mine isn't.

Can you create a mapping to the directory that contains App.cfc #1? If so, you may be able to extend "yourMappingName.application".

Both extends=".Application" and extends="/Application" should work if Application.cfc you need to extend is in the root.

In the root, create a file named AppProxy.cfc. Its contents are thus:
<cfcomponent output="false" extends="application" displayname="Application.cfc Proxy" hint="Extends the root application object so that subdirectories may extend it.">
</cfcomponent>
Then, in your subdirectory, set up your application.cfc to extend AppProxy.cfc. This will successfully inherit your root directory application.cfc methods.
<cfcomponent output="false" extends="AppProxy">
<cffunction name="onRequestStart" output="true">
<cfset super.onRequestStart() />
<!--- Some other stuff happens here. --->
</cffunction>
</cfcomponent>
This will work, by the way, even if the AppProxy isn't in the root directory. In that case, make sure your "child" application.cfc uses dot notation to find the AppProxy.
<cfcomponent output="false" extends="Path.To.Child.Directory.AppProxy">
<cffunction name="onRequestStart" output="true">
<cfset super.onRequestStart() />
<!--- Some other stuff happens here. --->
</cffunction>
</cfcomponent>

I use includes in onRequestStart and onApplicationStart.
That way when I am writing another Application.cfc, I can just include the code.

Related

When Extending an Application.cfc Through a Root Proxy How Do I Prevent <cfincludes> from Breaking?

I'm in the process of switching over from using Application.cfm to Application.cfc and I'm using Ben Nadel's method for extending my application into a sub folder using an application proxy. (link to article)
The problem I'm having is that when I load a page in a sub folder all cfinclude tags that are called in the root Application.cfc file bring up a "Could not find the included template..." error message. (The include is intentionally at the top of the component so I can set application specific variables)
Here are a few requirements:
The application has to be run without access to the ColdFusion administrator.
The application may or may not reside in a sub folder of another site (i.e. www.example.com/ or localhost/mysite/)
Here's the file structure:
/application.cfc
/include_me.cfm
/index.cfm
/sub/application.cfc
/sub/application_rootProxy.cfc
/sub/index.cfm
Root Application.cfm:
<cfcomponent
output="false"
hint="I define the application settings and event handlers.">
<!--- Define the application settings. --->
<cfset this.name = "TestApplication" />
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 10, 0 ) />
<!---
Store the path of the current template. We want to see if
this shows up as the root template or the sub template.
--->
<cfset this.ROOT_currentTemplatePath = getCurrentTemplatePath() />
<!--- Set a variable to indicate that the included file hasn't been run yet --->
<cfset this.includedFile = "no" />
<!--- include the file --->
<cfinclude template="include_me.cfm" />
<cffunction
name="onApplicationStart"
access="public"
returntype="boolean"
output="false"
hint="I initialize the application.">
<!--- Set some app variables for testing. --->
<cfset application.ROOT_onApplicationStart = true />
<!--- Return true so the page can process. --->
<cfreturn true />
</cffunction>
<cffunction
name="onRequestStart"
access="public"
returntype="boolean"
output="false"
hint="I initialize the request.">
<!--- Set some request variables for testing. --->
<cfset request.ROOT_onRequestStart = true />
<!--- Return true so the page can process. --->
<cfreturn true />
</cffunction>
<cffunction
name="onRequest"
access="public"
returntype="void"
output="true"
hint="I process the user's request.">
<!--- Define arguments. --->
<cfargument name="script"type="string" required="true"hint="I am the request script." />
<!--- Output the current THIS collection. --->
<cfdump var="#this#" label="THIS" />
<!--- Include (execute) requested script. --->
<cfinclude template="#arguments.script#" />
<!--- Return out. --->
<cfreturn />
</cffunction>
</cfcomponent>
Root Include_me.cfm:
<!--- update the value so we know the file was indeed included --->
<cfset this.includedFile = "yes" />
Sub Folder Application.cfc
<!--- extends the application so we can make changes when needed --->
<cfcomponent extends="application_rootProxy">
<cfset this.SUB_currentTemplatePath = getCurrentTemplatePath() />
</cfcomponent>
Sub Folder Root Proxy:
<cfinclude template="../application.cfc">
What is the correct way to allow cfinclude tags in the base application.cfc when you're accessing the application through a root proxy?
My initial instinct was to see if I could calculate the application root dynamically and luckily getCurrentTemplatePath() is able to differentiate between the sub application.cfc and the root application.cfc. However cfincludes don't work when you try and access them via a local file system link (e.g. d:\mysite\include_me.cfm). It looks like I need to somehow figure out the dynamic relative position of the included file based on the sub directory of the executing application.cfc. Any and all help is appreciated!
I may be on to something... and if this is the answer hopefully it will help someone else out who finds themselves in a similar predicament.
I noticed that the cfinclude within the OnRequest() method processes normally regardless of whether the template is being called from the root of the application or a sub directory. Therefore I theorized if I put my cfincludes within methods they might execute properly.
So instead of placing my cfincludes at the top of my root component:
<cfcomponent>
<cfinclude="include_me.cfm">
...
</cfcomponent>
I can put them in a separate method and then call that method within the component:
<cfcomponent>
<!--- call the method which includes the file --->
<cfset includeFile() />
<!--- new method for including the file --->
<cffunction name="includeFile">
<cfinclude="include_me.cfm">
</cffunction>
...
</cfcomponent>
The key to this seems to be not including anything in application.cfc unless it's contained within a method.
I'm not sure what's causing what you're seeing, but you do have an unorthodox way of proxying your Application.cfc
This doesn't solve your issue, but here is a demonstration of doing the proxying correctly, and this doesn't have the include pathing issue you're seeing.
All the code is here: https://gist.github.com/daccfml/3ed091c62d688595d66e
/Application.cfc
component {
writeOutput("#getCurrentTemplatePath()# called<br>");
include "inc.cfm";
}
/inc.cfm
<cfoutput>#getCurrentTemplatePath()# called<br></cfoutput>
/ApplicationProxy.cfc
component extends="Application" {
writeOutput("#getCurrentTemplatePath()# called<br>");
}
/sub/Application.cfc
component extends="ApplicationProxy" {
writeOutput("#getCurrentTemplatePath()# called<br>");
}
/sub/test.cfm
<cfoutput>#getCurrentTemplatePath()# called<br></cfoutput>
This outputs:
C:\wwwroot\Application.cfc called
C:\wwwroot\inc.cfm called
C:\wwwroot\ApplicationProxy.cfc called
C:\wwwroot\sub\Application.cfc called
C:\wwwroot\sub\test.cfm called
Which is what I'd expect.
Rearrange your code to do the proxying correctly, and hopefully your issue will disappear. If not, update your question and we can revisit.

Any one using Mura, how can I safely add a globalFunctions.cfc

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

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

coldfusion problem about onRequest method in Application.cfc

i was blocked by a coldfusion problem, any suggestions are appreciated. now lemme decribe my problem.
i have an Application.cfc in my website root, the content in it is as follows:
<cfcomponent output="false">
<cffunction name="onRequest" returnType="void">
<cfargument name="thePage" type="string" required="true">
<cfinclude template="#arguments.thePage#">
</cffunction>
</cfcomponent>
and also i have a cfm template of which the name is test.cfm, it's content is listed as follows:
<cfdump var="#variables.this#"><br /><br /><br /><br /><br /><br />
<cfdump var="#this#">
now if you request the test.cfm, everything is ok, but when i delete the onRequest method in Application.cfc and request test.cfm again, it complaints that "Element THIS is undefined in VARIABLES. ", i don't know why, can anybody explain it? great thanks.
ps:
you can add as many functions into Application.cfc, such as onSessionStart, onSessionEnd, onApplicationStart, onApplicationEnd...,
but if there is not a onRequest method, you request test.cfm and get error. i just don't know why.
It's because the this scope refers to a cfc instance. When you include test.cfm from within application.cfc this refers to the application.cfc instance. When you call test.cfm directly this does not exist because the request did not go through application.cfc, so you're not inside a cfc instance.
Not sure what you were trying to do, but you probably don't want to use this outside of a cfc. If you want to dump the application scope from test.cfm just do this instead:
<cfdump var="#application#"/>
Returning true from the onRequestStart method will load the page for you. As dwb stated your 'this' is referring to to the Application.cfc because you have included it from within one of the methods. If you need to refer to the Application use the application scope not 'this', unless you really are inside of the Application.cfc.

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>