Cannot locate component from cfinvoke statement - coldfusion

I had some ColdFusion code handed to me due to someone leaving the company that I was tasked to maintain. The problem is the last time I looked at some ColdFusion code was around 10 years ago so I'm completely lost and I'm starting from scratch.
My issue is I'm trying to locate the component or code and whatever is being called in the code shown below.
<cfinvoke component="_admin_tools_kms" method="listKMSUsers" returnVariable="getKMSUsers">
<cfinvokeargument name="argFullAccount" value="1" />
</cfinvoke>
I know this is returning a list of users for the system but I cannot locate the exact code making that call. Should that component name, "_admin_tools_kms", be the name of a cfm file somewhere or can that be just a reference name created somehow?

Let's break this down:
<cfinvoke
component="someComponent"
method="someFunction"
returnVariable="someVariable">
<cfinvokeargument name="someArgument" value="foo" />
</cfinvoke>
The attribute component is either name of a file with a .cfc extension or a variable containing an instance of that component. If it was a variable, it would be referenced as #someComponent#.
If there's just a simple value in there, odds are there's a file named someComponent.cfc in the same folder.
If there's a dot-separated value in there, like cfc.services.someComponent,
look for a folder at the web root named /cfc/ that contains another folder /services/ in which you'll find the file someComponent.cfc.
If you don't see that folder, look for a ColdFusion mapping in the root Application.cfc file that creates the alias cfc and maps it to the folders you want.
and if THAT's not there, then check your web server for folder aliases or mappings to see what the heck some psychopath did to hide where that code exists.
BUT, I prefer doing a simple text search for the method referenced instead of the component. That function could exist in another component or file than the one referenced.
Since a CFC can extend another CFC, there could be a parentComponent.cfc that someComponent extends which actually contains the method in question. Worse, the file someComponent.cfc could have other files included where one contains the method. The only downside is if the function is named something common like create.

Related

How to consume component from file in different folder

Working with version: 2016,0,10,314028
I know there are other questions addressing the issue, but since they are a bit old I wonder if anything has changed since then. Here is my problem:
In the "OnRequestStart" function inside Application.cfc (located in root folder), I have this:
<cfset application.cfclogin = "comp.login" />
"comp" begin the folder and login the name of the cfc file.
Inside the same Application I consume the cfc without a problem, like this:
<cfinvoke component="#application.cfclogin#" method="sgice_redirect" >
Even in a logout.cfm file located in root, the same cfinvoke method works.
But if a try the same cfinvoke from a file located in /subfolder/Layout.cfm for example, the cfinvoke throws an Error. Which looks like this:
I tried putting the name of the app in the dot notation declaration (APP.folder.cfcfile) but that doesn't work even in the files located in root invoking the cfc.
Other websites inside the same server are using this method of declaring the dot notation name for the cfc inside OnRequestStart and using that variable throughout the files, but I can't find the reason of this not working in this specific site. If you are wondering about the F:/ route, F:/WebSitesTij/CFSites/sit_tij/ is supposed to be my root folder in this case. Any help is appreciated.

How to get directory path up to a specific folder only?

Using: CF10 and IIS7.5
I have a section within my website called "Bookings". It is located like this:
c:\inetpub\wwwroot\mysite\bookings
Within this folder will be sub-folders and eventually webpages themselves. Heres an example:
c:\inetpub\wwwroot\mysite\bookings\holidays\new.cfm
c:\inetpub\wwwroot\mysite\bookings\carhire\edit.cfm
I include (using <cfinclude>) another page within each webpage that displays different links depending on which page is calling it. All I want to know is the directory up to the "bookings" folder. Something like this (pseudo code):
<cfset whereAmI = #GetDirectoryFromPath(GetBaseTemplatePath())#>
<cfif #whereAmI# EQ "C:\inetpub\wwwroot\mysite\bookings">
<h1>Booking Section Links</h1>
</cfif>
The above code works only if the user visits the bookings/index.cfm page of the "bookings" folder. But if they go to the bookings/holidays/new.cfm page, it is now in the holidays folder so the <h1> content will not appear. I really only want to check for any page that is in the bookings folder, even if it is within a subfolder within the bookings folder. A bit like in SQL where I could say IF #GetDirectoryFromPath(GetBaseTemplatePath())# LIKE 'c:\inetpub\wwwroot\mysite\bookings%' so it has a wildcard on the end.
I know this question is going to irritate the MVC framework advocates but please excuse me on this!
Here is a quick, easy way to solve your problem (may not work as system expands - but should get you started down the right path).
<cfset whereAmI = GetDirectoryFromPath(GetBaseTemplatePath())>
<cfif whereAmI CONTAINS "C:\inetpub\wwwroot\mysite\bookings">
<h1>Booking Section Links</h1>
</cfif>
Note, I removed the # from inside the cfset and cfif you do not need them there.
You could even scale back the path to use just 'mysite\bookings'.
Ideally, this should be wrapped up into a function so that you can easily pass different paths into it to determine if you are on a given page. Or, possibly, even determine the 'parent' folder in onRequestStart in Application.cfc and set it as a request scope variable.
This will need to be tweaked if you run the code on a *nix based system.
It is more easier with CGI variables. You can use "CF_TEMPLATE_PATH". Try this
<cfoutput>The value of CF_TEMPLATE_PATH is: </cfoutput><cfdump var="#CF_TEMPLATE_PATH#">

cfc that bring in additional functions via include

My application.cfc starts with:
<cfcomponent extends="org.corfield.framework">
later on void function setupApplication() output="false" {
I have
// tools
include "initapp.cfm";
initapp.cfm has nothing but functions in it. Things like:
<!--- Helper functions --->
<cfscript>
string function stripHTML(str) output="false" {
return REReplaceNoCase(arguments.str,"<[^>]*>","","ALL");
}
application.stripHTML = stripHTML;
</cfscript>
The nature of the functions is NOT associated with a session. Is there a better way to make functions available globally?
If you're trying to put helper functions together for use in CFCs, one option may be to use the component.cfc file.
Using the component.cfc file
All CFCs automatically extend the ColdFusion
WEB-INF/cftags/component.cfc component. (The WEB-INF directory is in
the cf_root/wwwroot directory on ColdFusion configured with an
embedded J2EE server. It is in the cf_root directory when you deploy
ColdFusion on a J2EE server.) This CFC is distributed as a zero-length
file. You can use it for any core methods or properties that you want
all CFCs in your ColdFusion application server instance to inherit.
Note: When you install a newer version of ColdFusion, the installation
procedure replaces the existing component.cfc file with a new version.
Therefore, before upgrading, you should save any code that you have
added to the component.cfc file, and then copy the code into the new
component.cfc file.
If that solution is TOO global you can extend your helper cfc, but it has to be done in every cfc and doesn't answer your one-time-set-it-and-forget-it idea. See Using CFCs effectively
If your helper functions are for use in .cfm files, I'd do like Adam suggested. I usually put my helper functions in a "tools" cfc located in a CFC folder and make it an application scoped cfc.
function onApplicationStart(){
application.tools = createObject("component", "cfc.tools");
}
One of my helper functions logs the time it takes to index a solr collection. Using it looks like
<cfset application.tools.logSolrIndex('collectionName',getTickCount()-start,qFileList.recordCount)>
Last resort:
If you had to stick with an include for use outside of the application.cfc, I might simply include initapp.cfm onRequest() before you include your page.
Put the functions in a library CFC, and then put an instance of that CFC in the application scope in onApplicationStart().
That said, you'd be kinda breaking encapsulation if you then referenced this application-scoped CFC within other CFCs, which is a consideration (not necessarily a deal breaker, but a consideration).
You could look at some sort of dependency injection approach to mitigate this (eg: ColdSpring)
Whichever way I ended up doing it, I would not be doing it the way you're doing it, I'm afraid.

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">

Extend a CFC using a relative path

I want to extend a CFC in a different directory and I have a couple of options, but can't figure out how to do this:
A) Use a dynamic mapping (this will have to be dynamic based on the site, e.g. for the live site it would be cfc.myPackage.MyCFC but on a dev site it would be myCfcRoot.myPackage.MyCFC) - I've tried putting expressions into the extends bit but obviously CF doesn't like that, e.g. :
<cfcomponent name="MyComponent" extends="#config.cfcRoot#.BaseComponent">
or
<cfcomponent name="MyComponent" extends="#GetRealPath(../BaseComponent.cfc)#">
B) Provide a relative path (somehow) to the CFC to extend.
I fear that I can't do this, but I'm hoping that there is something I've missed.
Daniel is basically correct, you need a mapping. However, there are 3 workarounds.
CFCs will pick the current path as a relative root mapping, so if your CFCs are all in the same directory, you can just say
<cfcomponent name="MyComponent" extends="Example">
Or if your components are in subdirectories from the current cfc, you can access it:
<cfcomponent name="MyComponent" extends="subdirectory.Example">
Second, if you are running on ColdFusion 8, you can define a mapping in your application.cfc using the mappings struct like this:
<cfset this.mappings["/MyApp"] = expandPath(".") />
There are two good references for Application.cfc, first, Ray Camden's example Application.cfc which just gives a nice view of what goes where, then the CF8 Live Docs application settings page, which has a section on mappings along with some good comments.
Finally, you can use the built-in mapping of your web root, so if your application is in a subdirectory named "MyApp" off the web root, your root mapping will be "MyApp". Let's say you correctly put your components in:
wwwroot\MyApp\com\MyApp\example.cfc
The mapping to this cfc in this case will be:
MyApp.com.MyApp.Example
And using your example, you can extend like this:
<cfcomponent name="MyComponent" extends="MyApp.com.MyApp.Example">
Anything else, such as if your components are outside of the web root, or if you are not sure what the folder structure of your finished application will be, and you will need to set a mapping in the CF Administrator.
Now this is only tested in cf8 so other engine could differ.
if you want to use relative paths to extend applications you can but your have to start them with a "/.". For instance you can do this to extend an application.cfc from your web root into directory below you webroot:
<cfcomponent output="false" extends="/.application">
<!--- whatever code you have --->
</cfcomponent>
now let's say I have the following paths in my application:
[webroot]/1/1a
[webroot]/2
let's say that the application.cfc in [webroot]/1/1a extends the application.cfc in [webroot]. now I want to place an application.cfc in [webroot]/2 and extend the application.cfc in [webroot]/1/1a. all i would have to do in my [webroot]/2/application.cfc is the following:
<cfcomponent output="false" extends="/./1/1a/application">
<!--- whatever code you have --->
</cfcomponent>
hope this makes sense.
Unless the CFC is in the same directory as the calling script the CFC must be located and referenced from a path relative to a "Mapping".
I have found that sometimes you need to make sure that "/" is mapped to your document root of your webserver and that becomes the base for all relative paths. Or you can setup a mapping for a CFC directory that hold all of your common CFCs.
This screen is found in the ColdFusion Admin under "Server Settings" -> "Mappings".
In regards to Method B. It can be done using an additional file in your example. It's one extra file per cfc that you want to extend per directory that you want to extend from.
The 3 files that are required.
/somepathtobasecomponent/basecomponent.cfc (your base component file)
/pathtoworkingcfc/function.cfc (the file with the extends attribute)
/pathtoworkingcfc/basecomponent_extend.cfc (the new file that will do the extending)
/somepathtobasecomponent/basecomponent.cfc
Nothing to change here. This stays the same.
/pathtoworkingcfc/function.cfc
in the cffunction tag set the extends to extends="basecomponent_extend"
/pathtoworkingcfc/basecomponent_extend.cfc
Sole content of the file is a cfinclude
<cfinclude template="/somepathtobasecomponent/basecomponent.cfc">