Store and use ColdFusion templates outside of the web root - coldfusion

I have been tasked with researching the possibility of storing .cfm files outside of the web root as a further security measure. (I'm not talking about CFCs, but the files that will appear in the URL string.)
I figured that I could use the properties of OnRequest() in the Application.cfc to do this, but no luck.
I've tried this:
<cffunction name="OnRequest" access="public" returntype="void" output="true">
<cfargument name="TargetPage" type="string" required="true"/>
<cfinclude template="C:/test#arguments.TargetPage#"/>
<cfreturn/>
</cffunction>
I then put a test.cfm inside C:\test and attempted http://localhost/test.cfm. That gives me a "file not found" error.
So then I created a ColdFusion mapping to C:\test called "test" and changed my CFINCLUDE thusly:
<cfinclude template="/test#arguments.TargetPage#"/>
Unfortunately I got the same error.
So, is what I'm after even possible? (Windows Server 2008 and IIS in case it matters. ColdFusion 9.)
I know that I could put a CFINCLUDE in my index.cfm and pass around URLs like http://www.example.com/?file=test.cfm, but I'd prefer not to do that.
Well here's something: If I put an empty test.cfm in my web root then this works. Obviously I'd really rather not put empty stubs for every one of my files (which, for this project, will end up being in the hundreds).

Rather than spinning your wheels down this path, let me point you in a possible better direction which may solve the original issue, but from a more preferred/best practices standpoint:
ColdFusion Sourceless Deployment
Rather than trying to jimmy-rig the site to read files outside of root, you simply never put the source code out into the world.

I think you would have better luck using onMissingTemplate rather than onRequest. http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7d2a.html

Related

Where is dump function in cfscript? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to do a cfdump inside a cfscript tag?
I'm new to Coldfusion and wondered if anyone knew of a way to access function when inside a script block. I cant find way of calling it from there.
Why have Adobe removed it? Or have they just called it something else now?
Sorry - I know this is probably really basic question, but like I said I'm new.
The script equivalent of cfdump is writeDump().
The same pattern has been used for cfoutput and cflog, with writeOutput() and writeLog() functions.
writeDump()
Apparently in ColdFusion 9 it has been added as writeDump() so you should be able to use this from a cfscript block.
So if you have CF9, then you are fine.
If you using an older legacy system which does not support this newly introduced writeDump() function, as we are, you could write a wrapper for it and put it somewhere accessible to all your files.
This is the wrapper I have written, which for short term debugging use can be put on the cfc file you are working on (although remove it before you commit your code - otherwise it's just a mess), or you can put it somewhere global so you can call it from shared scopes.
Here is an example of a wrapper function you can use:
<cffunction name="dump" access="private" returntype="void" hint="dump wrapper">
<cfargument name="stuffToDump" type="any" required="true" hint="what you want to dump">
<cfargument name="abort" type="any" default="false" hint="abort after dump">
<cfargument name="expand" type="any" default="false" hint="expand output">
<cfdump var="#arguments.stuffToDump#" expand="#arguments.expand#">
<cfif #arguments.abort# EQ 1>
<cfabort>
</cfif>
</cffunction>
There are probably better ways around this problem, but this is what I currently use.
You can put it (temporarily) on the cfc file that you are currently working on, but obviously don't commit this to your code base as you don't want the dump function on all your files.
Or you could put it permanently onto a cfinclude file, and just include that (again - temporarily) to files you are debugging.
Another alternative I guess is to put it onto the Application.cfc. This file has a number of standard methods but you can also define your own additional methods to be included into it. Then you have your dump() function available in the APPLICATION scope.
There's a good info page on the Application.cfc file here. http://www.bennadel.com/blog/726-ColdFusion-Application-cfc-Tutorial-And-Application-cfc-Reference.htm
I have even seen it used in the Server scope, but this should never be done on production code. OK for debugging I guess as a last resort, just make sure you remember to remove it.
Personally I think both these options are probably far from ideal, and it's a great shame it took Adobe so long to provide a script alternative to the function. Would have saved a lot of pain.
But hopefully if you are using CF9 then all this will be irrelevent to you and you can just use the new writedump() function now they have finally added it in.
To be a little clearer and provide an example:
<cfscript>
SomeString = 'ABC';
writeDump(SomeString);
</cfscript>
Peter's answer is 100% correct.

Mapping to a CFC in ColdFusion

In my application I have all my CFC's in a cfc folder. From the site root I can access them without any trouble by simply referring to them in my <cfinvoke> tag as component=cfc.mycomponent method=mymethod
The trouble is, when I want to access the cfc from another page that's not in the root I can't use component=../.cfc.mycomponent to get in touch with that cfc.
What am I doing wrong here?
There are a handful of options for getting this to work. Unfortunately, learning them has taken me a good amount of trial and error. Let me share what I've learned.
First, you can use the classic method of creating a mapping in your CF Administrator. Specify the exact path to your components (e.g. c:\wwwroot\cfc), and the mapping (pseudo-folder) that you want to call it by (e.g. MyCFCs). Now from anywhere in your application, you can reference create a new MyCFCs.mycomponent() (using CF9+'s new keyword, you can substitute for createObject("component","MyCFCs.mycomponent") to be compatible back to CF6).
The downsides to using a server mapping are that you have to configure this on every server your application runs on. I typically have a local development server which has a radically different configuration from my production servers, and making changes on production servers is a pain for me, so I try to avoid server mappings whenever possible.
Second, you can reference your CFCs from a web-root-relative path, meaning that if your application is in the root of your server and the /cfc path is directly off of the web root, you can always do new cfc.mycomponent() from anywhere in your application. ColdFusion 6.1 and up will correctly map to the root of your web site. This is like referencing an image using /images/mypicture.jpg, anywhere in your web site, /images will will go straight to the same directory.
The downside of using the web-root-relative path is that if your application will ever be in a different folder off of the web root, or will ever be in a subdirectory and sometimes be at the web root, the relative path from the web root will change, breaking these links.
Third, you can create an application-specific mapping. This was introduced in CF8 and requires that you have an Application.cfc file. It is simple to add. Raymond Camden has a great reference. The syntax is essentially like this.
<cfset this.name = "MyAppName"/>
<cfset this.mappings = structNew() />
<cfset this.mappings["/cfc"] = getDirectoryFromPath(getCurrentTemplatePath()) & "cfc/" />
The only downside to this method is that your Application.cfc can't extend a CFC in a mapped folder. It's an obscure problem, which probably won't affect you. Also, you will need to have an Application.cfc, which is good practice, but I don't know if you are doing that yet.
Fourth, you can instantiate your CFC into your application scope, probably from within the aforementioned Application.cfc, inside an OnApplicationStart() method. This moves any compile/instantiation time into your application's first hit, and removes it from subsequent hits. The code is very simple.
<!--- from Application.cfc, inside onApplicationStart() --->
<cfset application.myComponent = new cfc.myComponent() />
<!--- from anywhere else in your application --->
<cfset application.myComponent.callMyMethod() />
The downside to this one is that once your component is in Application memory, any changes you make to it while you are developing your application will not be reflected until you clear the application memory or call onApplicationStart() again. It's not hard to get around, but it's just more code, and more to manage.
One final note, you may want to think about moving from <cfinvoke> to createObject("component",...) or, if you are on CF9, new. The cfinvoke syntax is fine, but every time you invoke a component from a path, you are re-instantiating it, and it also is not a very object-oriented way to call your components. Food for thought, take it or leave it :)
You can't use relative paths with components.
What you need is a ColdFusion mapping. There are two ways to do this.
The first is to go into your ColdFusion administrator, go into the mappings section and add a /cfc mapping that points to your cfc folder.
The other way is to use application specific mappings;
In the Application.cfc for your application you can add application mappings as you would under the ColdFusion administrator mapping. At the top of your application cfc add a cfset of this.mappings as an array. In this array set the mapping with the directory path.
<cfset this.mappings["/cfc"] = GetDirectoryFromPath( GetCurrentTemplatePath() )&"cfc">
with the mapping of /cfc to your cfc folder any component calls to cfc.objectname will load the appropriate component in your cfc folder.
You absolutely can extend a cfc with mappings. I had to do it myself.
One of the most frustrating things that I have had to deal with in ColdFusion is trying to create an external application that is open to the general public and having to secure a portion of that site with an application within a subfolder and extending the logic from base application.cfc. I'll walk you through the current approach that developers use to solve this as well as showing you how to additionally use mapping when there may be a hosting provider that uses virtual directories.
This is a rather long article, if you want to jump to the condensed summary, scroll down to the bottom of this page.
Many years ago, the first time that I tried to perform this, I received the following message no matter what I tried: "Could not find the ColdFusion component or interface xxx'. In a nutshell, the problem using this approach is that both the root and the subfolders have the same name, i.e. Application.cfc, and ColdFusion can't properly identify what component to extend. Finally, after some serious investigation, someone came up with the idea to create a proxy.cfc that resides in the same root directory as the root Application.cfc, and the Application.cfc in the subfolder extends an empty proxy.cfc that extends the root cfc like so:
root directory: Application.cfc
This root Application.cfc does not extend anything
Also in the root directory: Proxy.cfc
Proxy.cfc has the following code, it's essentially empty. The only thing that the Proxy.cfc does is to extend the Application.cfc that is in the same directory:
<cfcomponent extends="Application">
</cfcomponent>
Subdirectory such as a folder named admin.
This subdirectory has another Application.cfc. Let's say that this component is responsible for securing the application and has login logic as well as debugging settings for example. This Application.cfc will extend the Proxy.cfc to gain the methods and properties of the Application.cfc in the root directory like so:
<cfcomponent displayname="Admin" extends="Proxy.cfc">
This approach was a godsend and it was heavily blogged about. Ben Nadel has made a number of very helpful posts which I will share at the bottom of this article.
This works quite well unless you're on a hosted domain or a server that uses virtual directories. In this case, we are in the same original boat in which we started from. Now we are back into the "Could not find the ColdFusion component or interface xxx' hell!
There is a solution for this tricky problem though, we need to also use mapping!
It is a common misnomer that you can't use mapping to extend components. I am not quite sure where this misconception originally came about, but it has been proven that this is just not true. There are occasions where we must use mapping to solve some annoying problems, like here.
This particular site is hosted by hostek.com. They are a fine company to deal with, but the server that my site is hosted on has some idiosyncrasies due to the directory structure. Here, when I use the Proxy.cfc method to extend the logic from the base Application.cfc to the Application.cfc in the admin folder I receive the dreaded 'could not find the ... component' error. When I first saw it I was dismayed thinking not this again, so I turned to ColdFusion CFC mapping. Mapping tells ColdFusion where to find the file and what the file relationships are.
Let's review CFC structure that was just discussed. For example, imagine the following directory structure:
root directory: i.e. www.gregoryalexander.com/
subdirectory: www.gregoryalexander.com/admin/
As discussed, we have an Application.cfc and the Proxy.cfc in the root directory, and we have the Application.cfc in the 'admin' subdirectory.
The Proxy.cfc extends the Application.cfc, also in the root directory, and the Application.cfc in the subdirectory (admin) extends the Proxy.cfc in the root directory.
root directory: contains both Application.cfc and Proxy.cfc (that extends the root Application.cfc).
subdirectory: Application.cfc (that extends Proxy.cfc).
Now we need to also add the following mapping in the root Application.cfc. This mapping logic should be near the top of the root Application.cfc, and it should not be within any of the Application.cfc event handlers (onApplicationStart, onApplicationRequest, etc). This mapping code does not need to be anywhere else other than the root Application.cfc:
<cfset this.mappings = structNew() />
<cfset this.mappings["rootCfc"] = getDirectoryFromPath(getCurrentTemplatePath()) />
<cfset this.mappings["adminCfc"] = getDirectoryFromPath( getCurrentTemplatePath() & "/admin" ) />
I used rootCfc to identify the Application.cfc in the root directory, whereas adminCfc applies to the Application in the admin directory. These variables can be named anything. Note that the "/admin" string at the end of the adminCfc mapping points to the 'admin' folder, which is a subdirectory.
Now that we have the mappings in the root Application.cfc, we need to apply them to the extends statement in Application.cfc located in the subdirectory. In the /admin/Application.cfc template use:
/admin/Application.cfc
<cfcomponent displayname="xxx" sessionmanagement="xx" clientmanagement="xx" extends="rootCfc.Proxy">
Of course, rootCfc tells the Application.cfc in the subdirectory to look for the Proxy.cfc template in the root directory. Like other 'extend' statements, you don't need to specify '.cfc' at the end of Proxy.
You don't need to use this 'extend' mapping in either the root Proxy.cfc or Application.cfc templates. They can already find each other as they are both in the same root directory.
/Proxy.cfc
<cfcomponent extends="Application">
</cfcomponent>
Summary
For the sake of absolute clarity:
root Application.cfc
Contains the mapping logic. Has the mappings for both of the root and subdirectory.
Does not use an 'extend' statement
<cfset this.mappings = structNew() />
<cfset this.mappings["rootCfc"] = getDirectoryFromPath(getCurrentTemplatePath()) />
<cfset this.mappings["adminCfc"] = getDirectoryFromPath( getCurrentTemplatePath() & "/admin" ) />
root Proxy.cfm
A simple 'extends="Administrator" works.
No mapping logic.
<cfcomponent extends="Application">
</cfcomponent>
subdirectory Application.cfc
The extends statement must be the mapping variable name of the folder (rootCfc), a dot (.), and finally the name of the Proxy.cfc template without the .cfc prefix (Proxy)
<cfcomponent displayname="Admin" sessionmanagement="yes" clientmanagement="yes" extends="rootCfc.Proxy">
My apologies for being so verbose. I annoyed myself while writing this- but not as annoyed when I was while trying to solve this problem!
Take care!
Make sure if your components extend other components the full path is used.
ie a Products controller
<cfcomponent displayname="Products" hint="Handles all product requests" extends="core.controller.controller" output="false">

CFC extends sibling folder

I've seen all kinds of solutions for extending cfcs in parent folders with access to parent files or the CF administration, but I have not seen a workable solution to extend a cfc in a "shared"/sibling folder without access to parent folders.
This solution requires access to parent folders? (Not sure what these mappings are, but I have no access to Application.cfc anyway)
This solution requires being able to create an application.cfc which doesn't work for me (creating one in myApp does nothing because the environment I'm in includes the index page in myApp and builds the from there... the client never directly calls it to fire recognition of Application.cfc)
For instance:
wwwroot/some/path/myApp/Shared/Base.cfc
wwwroot/some/path/myApp/Function/Function.cfc
I'm looking to be able to call functionality in the Base.cfc (which contains common methods used in the application) from the Function.cfc via super and extension.
I have access to all files within myApp, but not "wwwroot", "some", or "path."
In order to extend Base.cfc within Function.cfc I have to extend the full path of "some.path.myApp.Shared.Base" This would cause problems if someone renamed the myApp folder since I would have to hand edit every Function.cfc that extends that Base.cfc
What I'm looking to do it create an application specific "barrier" so if the app folder name changes it will not cause mass havoc that requires I edit all the CFC files that use functionality from the Base.cfc.
Since I can't use relative paths to the Base ("..Shared.Base") I'm wondering if there's a way to create a CFC in the myApp folder that I can extend from and alleviate the renaming headache if it were to occur or a way to give it a generic name like "myApp" and extend from there. (myApp.Shared.Base)
I do not have access to Application.cfm, nor the Coldfusion administration.
Personally I would go simpler way: incapsulate the Base into the Function.
Looks like you want to use the set of core components for some common functionality. If this is true -- incapsulation even more applicable.
Paths to objects can be built dynamically, for example (step-by-step process for easier reading):
<cfscript>
path1 = GetDirectoryFromPath(cgi.SCRIPT_NAME);
path2 = ListToArray(path1, "/");
path3 = path2;
path3[ArrayLen(path3)] = "shared";
path4 = ArrayToList(path3, ".");
path5 = ArrayToList(path2, ".");
myBase = CreateObject("component", "#path4#.Base");
myFunction = CreateObject("component", "#path5#.Function").init(myBase);
</cfscript>
In the Function create function init:
<cffunction name="init">
<cfargument name="base">
<cfset variables.Base = arguments.base />
<cfreturn this />
</cffunction>
Of course, you may have strong reasons for extending, but at least this approach is not sensitive to the parent directories renaming.
If Base.cfc doesn't extend another cfc then you may be able to include the Base.cfc file into another cfc file in your function folder.
For example create a cfc file in the function folder with content:
<cfinclude template="../shared/base.cfc" />
Then extend the new file instead of the cfc in the shared folder.
The easiest way I can see to do this is to create a symlink or junction point to the base directory in the extension function directory.
Unfortunately, it's not a pure CF solution, nor portable if you need to move your code. Hopefully, someone will have a better answer, but this could be a fallback point if none is presented.
Generate the code on app start/reset...
For example the component tag could be like so in a file that should not be run directly...
<cfabort>
<cfcomponent extends="{sharedpath}.Base">
...
then in a function in or called from application.cfc do something like this...
<cfdirectory name="codetemplates" action="list" directory="wwwroot/some/path/myApp/codetemplates" />
<cfloop query="codetemplates">
<cffile name="temp" action="read" path="#tempfilepath##filename#" />
<cfset newfilecontent = replace(temp.filecontent, '{sharedpath}', configvarwithrightpath) />
<cfset filecontent = replace(newfilecontent , '<cfabort>', '') />
<cffile name="temp" action="write" path="#livefilepath##filename#" />
</cfloop>
The other approach is to have a build process locally that you run to create the necessary code but my example above would allow you to update a config variable and update the cfc's as needed in a live environment.
Why bother putting the shared code in a separate folder? If you just put it in the same folder as your "functions" cfcs then they can all extend it using a relative path.
So instead of:
wwwroot/some/path/myApp/Shared/Base.cfc
wwwroot/some/path/myApp/Function/Function.cfc
Use:
wwwroot/some/path/myApp/Function/Base.cfc
wwwroot/some/path/myApp/Function/Function.cfc
and:
<cfcomponent extends="Base"></cfcomponent>
However, if you have/need multiple "function"-level folders, then you can do something similar. Put a Proxy.cfc inside each "function"-level folder, with this code:
<cfcomponent extends="some.path.myApp.shared.Base"></cfcomponent>
And then each cfc in the "function"-level folders would extend their local Proxy.cfc (extends="proxy"). This gives you 1 Proxy per folder, which is still not ideal, but less of a hassle than updating every cfc.

Fusebox installation without access to webroot

Previously (and locally) I've placed the fusebox5 directory in the web root, and then applications from anywhere in the tree have been able to access it. I'd previously also used Application.cfm rather than .cfc.
In this environment I don't have access to the webroot and the FB files don't really need to be that far down anyway, so I had planned to store them alongside the applications. Am I right in thinking that index.cfm is overlooked if Application.cfc is in use (and therefore there's no point changing the cfinclude value to be eg. ../fusebox5/)? If so, how can I include the framework without having Fusebox in the root or in a mapping? Error is:
Could not find the ColdFusion Component or Interface fusebox5.Application.
No, your app is still going to need index.cfm. What you'll need is a cf application mapping for Fusebox in your Application.cfc. Look at Ray Camden's Application.cfc template for an example of setting application specific mappings.
You can run Fusebox 5+ in a subfolder of your app root. It just may not be the most obvious thing to make it work.
Unfortunately, you cannot create a dynamic mapping for extending Application.cfc because your Application.cfc has not yet been instantiated - you get a chicken vs. egg scenario. You can't create the mapping to Fusebox because your Application.cfc didn't start, you can't start your Application.cfc because it can't find the component it's supposed to extend.
THIS IS A BUG IN COLDFUSION 8. ColdFusion should look for mappings in this order:
Mapped folders from the CF Administrator
Sub directories off the current directory
Sub directories off the web root called
It does this when you use CreateObject(), but not when you use the Extends attribute on cfcomponent.
The easiest solution is to use your Application.cfc like you would for any application, then include fusebox from your index.cfm. Your folder structure would look like this:
/myapp/fusebox5/
/myapp/index.cfm
-- consists of <cfinclude template="fusebox5/fusebox5.cfm" />
Your index.cfm file will not be ignored as long as you don't intercept the request with Application.cfc's OnRequest, or if you use OnRequest, make sure you include the intended target (which will almost always be index.cfm anyway).
If you want to not require index.cfm to do the include, you can have your Application.cfc's OnRequest method do the cfinclude.
<cffunction name="onRequest">
<cfinclude template="fusebox5/fusebox5.cfm">
</cffunction>
You still may need an index.cfm so your web server won't give a directory listing or 404, but it's ok if the file is empty.
In Application.cfc:
<cfscript>
this.mappings = {}; //create a new structure to store app-specific mappings
this.mappings["Fusebox"] = expandPath('./Fusebox'); //add mapping
</cfscript>

Extending application.cfc in a subdirectory

I have the following two files and would like the second to extend the first:
wwwroot\site\application.cfc
wwwroot\site\dir\application.cfc
However, when I go to declare the component for the second file, I'm not sure what to put in the extends attribute. My problem is that several dev sites (with a shared SVN repository) are running off the same instance of ColdFusion, so I can't just create a mapping in the CF admin like so:
<cfcomponent extends="site.application">
However, ColdFusion doesn't like:
<cfcomponent extends="..application">
or any dynamic input like:
<cfcomponent extends="#expandpath('..').#application">
Creating a runtime mapping (like here) doesn't seem possible either. Creating it in the base application.cfc is useless because that code hasn't yet executed by the time the inheriting cfc is being declared; and I can't create the mapping before the inheriting component is defined because there isn't yet an application to attach it to.
Is there any way I can reference the parent directory to accomplish my extends?
Edit to clarify: The ApplicationProxy solution doesn't work because of the bolded text above. Right now, as a workaround, we're simply not checking the \dir\application.cfc into SVN so that each developer can keep a version that extends his/her own root application.cfc. Obviously, this is not ideal.
Sean Corfield has a blog entry explaining how to extend a root Application.cfc.
Below is the relevant information copied from that entry.
Here's your root CFC /Application.cfc:
<cfcomponent>
<cfset this.name = "cf7app" />
<cfset this.sessionmanagement = true />
</cfcomponent>
Here's your proxy CFC /ApplicationProxy.cfc:
<cfcomponent extends="Application">
</cfcomponent>
It's completely empty and serves merely to create an alias for your root /Application.cfc. Here's your subdirectory CFC /app/Application.cfc:
<cfcomponent extends="ApplicationProxy">
<cffunction name="onSessionStart">
<cfoutput><p>app.Application.onSessionStart()</p></cfoutput>
<cfset session.counter = 0 />
</cffunction>
<cffunction name="onRequestStart">
<cfoutput><p>app.Application.onRequestStart()</p></cfoutput>
<cfdump label="application" var="#application#"/>
</cffunction>
</cfcomponent>
The root of each individual site should have its own Master App:
/site1/Application.cfc
/site2/Application.cfc
/site3/Application.cfc
All these applications are separate individual apps with nothing shared between them.
If any of these individual sites need to have sub-applications, then there should be ApplicationProxy.cfc alonside the Master,
e.g.
/site1/ApplicationProxy.cfc
/site2/ApplicationProxy.cfc
Then, for each sub-application you have the one that extends the proxy:
e.g.
/site1/subA/Application.cfc
/site1/subB/Application.cfc
/site2/subA/Application.cfc
The following code is working for me. One thing I noticed though is that the application.cfc seems to get cached, so changes to the parent application cfc might not be reflected. I got around this by doing a trivial change to the child application cfc.
<cfcomponent output="false">
<cfset variables.higherPath = ReReplace(GetMetaData(this).name,"\.[^\.]+\.[^\.]+$","") />
<cfset variables.extendApp = CreateObject("component", "#variables.higherPath#.Application") />
<cfloop item="variables.key" collection="#variables.extendApp#">
<cfif IsCustomFunction(variables.extendApp[variables.key])>
<cfset super[variables.key] = variables.extendApp[variables.key]>
<cfelse>
<cfset this[variables.key] = variables.extendApp[variables.key] >
</cfif>
</cfloop>
<cffunction name="onApplicationStart" output="false">
<cfset super.onApplicationStart() />
</cffunction>
I know this is an old topic, but I found a way to do it (that seems to work in my testing) without using the CF Administrator mappings.
You can do this by adding a per-application mapping in your child Application.cfc using an expanded relative path:
<cfcomponent extends="cms.Application" output="false">
<cfset this.mappings["/cms"] = expandPath(getDirectoryFromPath(getCurrentTemplatePath()) & "../../../../")>
<cflog text="#getMetadata(this).extends.path#">
</cfcomponent>
Yeah, it feels little hacky, but it seems to work.
Edward, et-al, I referred to your comment in the post below. See https://gregoryalexander.com/blog/2021/1/30/Extending-Applicationcfcs-using-mappings-and-proxies
You absolutely can extend a cfc with mappings. I had to do it myself.
One of the most frustrating things that I have had to deal with in ColdFusion is trying to create an external application that is open to the general public and having to secure a portion of that site with an application within a subfolder and extending the logic from base application.cfc. I'll walk you through the current approach that developers use to solve this as well as showing you how to additionally use mapping when there may be a hosting provider that uses virtual directories.
This is a rather long article, if you want to jump to the condensed summary, scroll down to the bottom of this page.
Many years ago, the first time that I tried to perform this, I received the following message no matter what I tried: "Could not find the ColdFusion component or interface xxx'. In a nutshell, the problem using this approach is that both the root and the subfolders have the same name, i.e. Application.cfc, and ColdFusion can't properly identify what component to extend. Finally, after some serious investigation, someone came up with the idea to create a proxy.cfc that resides in the same root directory as the root Application.cfc, and the Application.cfc in the subfolder extends an empty proxy.cfc that extends the root cfc like so:
root directory: Application.cfc
This root Application.cfc does not extend anything
Also in the root directory: Proxy.cfc
Proxy.cfc has the following code, it's essentially empty. The only thing that the Proxy.cfc does is to extend the Application.cfc that is in the same directory:
Subdirectory such as a folder named admin.
This subdirectory has another Application.cfc. Let's say that this component is responsible for securing the application and has login logic as well as debugging settings for example. This Application.cfc will extend the Proxy.cfc to gain the methods and properties of the Application.cfc in the root directory like so:
This approach was a godsend and it was heavily blogged about. Ben Nadel has made a number of very helpful posts which I will share at the bottom of this article.
This works quite well unless you're on a hosted domain or a server that uses virtual directories. In this case, we are in the same original boat in which we started from. Now we are back into the "Could not find the ColdFusion component or interface xxx' hell!
There is a solution for this tricky problem though, we need to also use mapping!
It is a common misnomer that you can't use mapping to extend components. I am not quite sure where this misconception originally came about, but it has been proven that this is just not true. There are occasions where we must use mapping to solve some annoying problems, like here.
This particular site is hosted by hostek.com. They are a fine company to deal with, but the server that my site is hosted on has some idiosyncrasies due to the directory structure. Here, when I use the Proxy.cfc method to extend the logic from the base Application.cfc to the Application.cfc in the admin folder I receive the dreaded 'could not find the ... component' error. When I first saw it I was dismayed thinking not this again, so I turned to ColdFusion CFC mapping. Mapping tells ColdFusion where to find the file and what the file relationships are.
Let's review CFC structure that was just discussed. For example, imagine the following directory structure:
root directory: i.e. www.gregoryalexander.com/
subdirectory: www.gregoryalexander.com/admin/
As discussed, we have an Application.cfc and the Proxy.cfc in the root directory, and we have the Application.cfc in the 'admin' subdirectory. The Proxy.cfc extends the Application.cfc, also in the root directory, and the Application.cfc in the subdirectory (admin) extends the Proxy.cfc in the root directory.
root directory: contains both Application.cfc and Proxy.cfc (that extends the root Application.cfc).
subdirectory: Application.cfc (that extends Proxy.cfc).
Now we need to also add the following mapping in the root Application.cfc. This mapping logic should be near the top of the root Application.cfc, and it should not be within any of the Application.cfc event handlers (onApplicationStart, onApplicationRequest, etc). This mapping code does not need to be anywhere else other than the root Application.cfc:
I used rootCfc to identify the Application.cfc in the root directory, whereas adminCfc applies to the Application in the admin directory. These variables can be named anything. Note that the "/admin" string at the end of the adminCfc mapping points to the 'admin' folder, which is a subdirectory.
Now that we have the mappings in the root Application.cfc, we need to apply them to the extends statement in Application.cfc located in the subdirectory. In the /admin/Application.cfc template use:
/admin/Application.cfc
Of course, rootCfc tells the Application.cfc in the subdirectory to look for the Proxy.cfc template in the root directory. Like other 'extend' statements, you don't need to specify '.cfc' at the end of Proxy.
You don't need to use this 'extend' mapping in either the root Proxy.cfc or Application.cfc templates. They can already find each other as they are both in the same root directory.
/Proxy.cfc
Summary
For the sake of absolute clarity:
root Application.cfc
Contains the mapping logic. Has the mappings for both of the root and subdirectory.
Does not use an 'extend' statement
root Proxy.cfm
A simple 'extends="Administrator" works.
No mapping logic.
subdirectory Application.cfc
The extends statement must be the mapping variable name of the folder (rootCfc), a dot (.), and finally the name of the Proxy.cfc template without the .cfc prefix (Proxy)
My apologies for being so verbose. I annoyed myself while writing this- but not as annoyed when I was while trying to solve this problem!
Take care!