Calling CFC's on another folder level - coldfusion

I have a page that I use cfc's on. Like this:
<cfset cfcDashboard = new dashboard()>
<cfset grab_image = cfcdashboard.getPicture()>
How do I call the cfc's if they are inside of a folder? As of right now they only work if they are on the same level or inside the same folder? How do you call a cfc that is on a different level?
Or am I not understanding the purpose of the cfc?

The new keyword is syntactic sugar for this call:
<cfset cfcDashboard = createObject("component", "Dashboard")>
The rules how ColdFusion resolves CFC names are in the docs.
If you use a cfinvoke or cfobject tag, or the CreateObject
function, to access the CFC from a CFML page, ColdFusion searches
directories in the following order:
Local directory of the calling CFML page
Web root
Directories specified on the Custom Tag Paths page of ColdFusion Administrator
You can use dot notation that corresponds to any of the defined search paths.
<cfset myDashboard = createObject("component", "my.custom.Dashboard")>
<cfset myDashboard = new my.custom.Dashboard()>
Will find (where . means the current template directory and / means the web root):
./my/custom/Dashboard.cfc
/my/custom/Dashboard.cfc
any/custom/tag/path/my/custom/Dashboard.cfc
Going "up" is not possible.

Granted there is no way to go "UP" but there is a way you can start at the top level of your website.
Place this line in your root Application.cfc file
<cfset this.directory = getDirectoryFromPath( getCurrentTemplatePath() ) />
<cfset this.mappings['/app'] = this.directory />
Then when you type your cfinvoke line type it like this
Of course, you want to replace [name of subfolder] with the folder your component is located and [name of component] when the name of your component. Everything else is the normal cfinvoke syntax.
So in a roundabout way, there is a way to start "up" from a sub-folder.

Related

How to add path with dots in CreateObject Argument

I'm trying to import the path from my component, but one folders has multiple dots, like board.event.calendar. Since this is the regular structure in this code base, I just can't change the folder name. I have tried multiple ways to achieve the right path in CreatObject argument, such as:
<cfset Event = CreateObject("Component", "path.to.'board.event.calendar'.Event") />
<cfset Event = CreateObject("Component", "path/to/'board.event.calendar'/Event") />
<cfset Event = CreateObject("Component", "path\to\'board.event.calendar'\Event") />
<cfset Event = CreateObject("Component", "path.to.board#chr(046)#event#chr(046)#calendar.Event") />
But no luck so far. How can I manage that?
CreateObject() uses the period character as the separator in your path, and will not accept invalid characters such as the single quote (as in your example above).
One way to do this is to create a mapping, and then use that in your path. For example, if the path to your application is C:\dev\myapp\, and the path to your calendar CFCs are in C:\dev\myapp\calendar\, then you can create a mapping in your application.cfc file like this:
this.mappings = {
"/app": "C:/dev/myapp/",
"/calendar: "C:/dev/myapp/calendar/"
}
(You can also create mappings in the CF Administrator)
And then when creating your component, you could use either:
myComponent = createObject("app.calendar.event").init();
or
myComponent = createObject("calendar.event").init();

URL Rewrite with IIS using ColdFusion

I've done a bit of searching but just can't put it all together. Here's what I need:
I'd like someone to surf to:
www.mysite.com/thisPlace
and have it redirect them to
www.mysite.com/template.cfm?pm=ms&loc_id=4
To do so, I somehow need to capture that they didn't request an existing file in their http request and run a .cfm page that queried the database for a record where locationName = 'thisPlace' and then redirect them to a page like
template.cfm?pm=ms&loc_id=4, where 4 is the record id of the row that matched 'thisPlace'
If your default document in IIS is set to index.cfm you could create a folder (directory) called "thisPlace" and place an index.cfm file that contains nothing but a <cflocation> tag and the accompanying query/logic to figure the URL.
Website.com/thisPlace would then function as you describe.
Edit:
You could add a custom 404 page...
Make it a .cfm file instead of html. Scan the template path to see what the user is looking for. If you find it in your database, redirect them there, else redirect them to a general 404 page.
<!---Up to a certain point (the directory in which you store your code) this will always be the same so you can hard-code your number --->
<cfset QueryConstant = #LEFT(CGI.CF_Template_Path, 22)#>
<!---Find the overall length of the template path. --->
<cfset QueryVariable = #Len(CGI.CF_Template_Path)#>
<!---Take whatever is past your QueryConstant (AKA the string that produces a 404 error.) --->
<cfset theRightNumber = QueryVariable - 22>
<cfset QuerySearchString = #RIGHT(CGI.CF_Template_Path, theRightNumber)#>
<cfquery name="ListOfLocations" datasource="CRM">
SELECT TOP 1 LocationID
FROM LocationTable
WHERE LocationName LIKE '%#QuerySearchString#%'
</cfquery>
<cfif ListOfLocations.recordcount>
<cflocation url="/SomePage.cfm?LocationID=#ListOfLocations.LocationID#">
<cfelse>
<cflocation url="/Regular404page.html">
</cfif>
Thanks guys! Huge help! Using your inputs, here's what I did:
(had to use QUERY_STRING instead of CF_Template_Path, as CF_Template_Path did not pass along anything after the url of the custom error page.
I set up a custom 404 error Execute URL in IIS to a file named check404error.cfm.
When someone looks for www.example.com/thisPlace, IIS sends them to http://www.example.com/check404error.cfm. I use the CGI.QUERY_STRING (404;http://www.example.com:443/thisPlace) to ultimately get the "thisPlace" string to search with.
<!---Up to a certain point (the directory in which you store your code) this will always be the same so you can hard-code your number --->
<cfset QueryConstant = #LEFT(CGI.QUERY_STRING, 31)#>
<!---Find the overall length of the template path. --->
<!---31 is the length of '404;http://www.example.com:443/' --->
<cfset QueryVariable = #Len(CGI.QUERY_STRING)#>
<!---Take whatever is past your QueryConstant (AKA the string that produces a 404 error.) --->
<cfset theRightNumber = QueryVariable - 31>
<cfset QuerySearchString = #RIGHT(CGI.QUERY_STRING, theRightNumber)#>
<cfquery name="ListOfLocations" datasource="#request.dsn#">
SELECT location.id
FROM location WHERE url_name = <cfqueryparam value="#QuerySearchString#" cfsqltype="CF_SQL_VARCHAR" maxlength="255"> LIMIT 1
</cfquery>
<cfif ListOfLocations.recordcount>
<cflocation url="https://example.com/template.cfm?pm=ms&loc_id=#ListOfLocations.id#" addtoken="no" statusCode="301">
<cfelse>
<cflocation url="/404error.cfm" addtoken="no">
</cfif>
This is how most popular MVC Framework work today, by parsing out the URL segments.
Do you have access to any kind of URL rewrite software?
Since you are using IIS it has a built in rewrite engine where you could simply rewrite these kind of requests to a known file saving you the overhead of sending a 404 reply and parsing that out and having more request created as a result of that.
See http://wiki.coldbox.org/wiki/URLMappings.cfm for details. We use the Isapi rewrite version version to do just what you are asking for
Receive request for www.mysite.com/thisPlace
thisPlace isn't a directory
thisPlace isn't a file
Resend to index.cfm or a location of your chosing for additional parsing
Helicon Rewrite sends an HTTP header named HTTP_X_REWRITE_URL with the original requested URL so parsing it out is then very easy.
This all happens inline withing the one request so the client is never redirected.

How to build ajax paths for .cfc files in a plugin?

In my plugin, I have defined a custom remote function. Here are the contents:
<cfcomponent output="false">
<cffunction name="getPersons" access="remote" output="false">
<cfset var local = {}>
<cfquery name="local.getPersons" >
SELECT FirstName FROM Persons
</cfquery>
<cfreturn local.getPersons>
</cffunction>
</cfcomponent>
This function is defined in file mytest.cfc that is stored under displayObjects/cfc directory.
In my main.cfm file that is stored in displayObjects directory, I need to make a ajax call to the function in mytest.cfc.
I am wondering how can I build proper path.
Calling #$.getCurrentURL()# returns:
http://localhost:8888/mymura/default/
However, I would somehow need to build and specify the following specify URL path to my ajax query:
http://localhost:8888/mymura/plugins/MyTest1_2/displayObjects/cfc/mytest.cfc
Can someone please tell me what is the recommended way to store plugin-specific .cfc files and how to properly build full path to access them?
Thank you in advance for your help.
Regards,
Peter
EDIT: I am almost there:
<cfoutput>
My other file
</cfoutput>
This returns:
http://localhost:8888/mymura/default/MyTest1_2/displayObjects/cfc/mytest.cfc
Instead of:
http://localhost:8888/mymura/plugins/MyTest1_2/displayObjects/cfc/mytest.cfc
Method $.currentURL() returns localhost:8888/mymura/default. I just need to find a way to traverse up one directory and append "plugins."
Any help is appreciated.
You can construct the URL using the domain setting of the site:
$.siteConfig().getDomain() & "/plugins/" & pluginConfig.getDirectory() & /displayObjects/cfc/myquery.cfc

How do I access a UDF library in the APPLICATION scope using a shortened name?

I am using ColdFusion 8.0.1.
I created a UDF library and put it in a CFC. I load the library in the APPLICTION scope like this:
// CREATE STRUCTURE OBJECTS
if (not isDefined("APPLICATION.AppOBJ") or not isStruct(APPLICATION.AppOBJ)) {
APPLICATION.AppOBJ = structNew();
APPLICATION.AppOBJ.udf_library = createObject("component", "udf.udf_library");
}
The library works great! But I want to reduce the code needed to access the functions, to shorten the reference. Currently, I have to access the functions like this:
APPLICATION.AppOBJ.udf_library.myFunction();
I want to be able to reference this library object as "UDF", like this:
UDF.myFunction();
In another ColdFusion 9 project (Again, this is a CF8 project!), I am able to do this right after I create the ojbect
<cfset udf = APPLICATION.AppOBJ.udf_library>
In the current project, this doesn't work in the application.cfm file. It DOES however, work when I put it on the page that it is being used.
My question is how far upstream can I put this last line of code to have the variable available on any page in the application? Is there a difference between CF8 and CF9 for this type of thing? Is the difference because I am working in application.CFM versus application.CFC?
Thanks!!!
-- EDIT -- MORE INFORMATION ---
The files that I am trying to access the APPLICATION.AppOBJ.udf_library object are within a custom tag. Might that matter?
-- ANSWER -- THANKS TO MICAH AND BEN NADEL ---
I haven't tried this yet but I think it should work as the idea comes from Ben Nadel's blog entry entitled Creating Globally Accessible User Defined Functions In ColdFusion (Safer Version)
<cfcomponent output="false" hint="I define the application settings and event handlers.">
<!--- Define the application. --->
<cfset this.name = "TestApp" >
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) >
<!---
Add all of our "global" methods to the URL scope. Since
ColdFusion will automatically seach the URL scope for
non-scoped variables, it will find our non-scoped method
names.
--->
<cfset structAppend( url, createObject( "component", "udf.udf_library" ) ) >
</cfcomponent>
You should now be able to access MyFunction() globally.
If you want to access the function as UDF.MyFunction() then I think you should be able modify Ben's example to the following:
<cfset UDF = StructNew() >
<cfset structAppend( UDF, createObject( "component", "udf.udf_library" ) ) >
<cfset structAppend( url, UDF ) >

How to include UDF_library in application.cfm?

I am using ColdFusion 8.0.1
I am working on an existing application that has thousands of pages. I am trying to include a library of new UDFs in my application.cfm file.
I addedd this line to application.cfm:
<!--- UDF library include --->
<cfinclude template="UDF/udf_library.cfm">
The UDF library includes other files that contain UDFs, like this:
<cfinclude template="udf_powerreview.cfm">
I have functions in the udf_powerreview.cfm file, such as:
// CREATE POWER REVIEWS SNIPPET
function createPRSnippet(Page_ID) {
LOCAL.Page_ID = ARGUMENTS.Page_ID;
if (isNumeric(LOCAL.Page_ID) && LOCAL.Page_ID > 0) {
LOCAL.Snippet = "<div class='pr_snippet_product'><script type='text/javascript'>var pr_snippet_min_reviews = 0; POWERREVIEWS.display.snippet(document, { pr_page_id : '#LOCAL.Page_ID#' });</script></div>";
} else {
LOCAL.Snippet = "";
}
return LOCAL.Snippet;
}
The debugging tool says that UDF/udf_library.cfm and udf_powerreview.cfm are being successfully included.
The problem is when I call the function in another page, I get an error that says that function doesn't exist. When I can copy the function and put it directly into the page that it is used in and it works just fine. And, I do not get the error "routines can not be declared twice".
In every site that I build, I create a udf_library.cfm or udf_library.cfc in the exact same manner. They always work fine.
What might prevent the functions from being available and accessed? Is there an application setting that needs to be set?
It's a page scope issue. Don't think of the Application.cfm as an include on all your pages, just know that it runs first. Somethings it initializes will carry over to your existing page scope and some things won't. Using an Application.cfc instead of an application.cfm takes care of much of the ambiguity.
To make your UDF's available to your whole application, I would suggest using a "Singleton" Design pattern. First take your UDF's and put them in a CFC format. This will make them more portable.
in your application.cfm you could put the following lines:
<cfif NOT isdefined('session.udf_powerreview') or isdefined('url.resetudf')>
<cfset session.udf_powerreview = createobject('Component','udf.udf_powerreview')/>
<!--- this 'udf.udf_powerreview' represents the physical path udf/udf_powerreview.cfc --->
</cfif>
I'm stuffing it in the session scope instead of the application scope, becuase you won't have an good way of resetting the application scope if you modify your UDF's.
Either way, once this is in your application.cfm you should be able to see your functions on any page.
<cfdump var="#session.udf_powerreview#">
Here is one strategy that I use. This basically calls the UDFs "on demand". It won't reimport the UDFs if it already exists. You do however have to have named arguments however, otherwise you'd have to strip out the UDFName out of the argument collection. I'm worried however that argument order might not be preserved, I haven't investigated that.
application.cfm
<cfapplication
name="udftest_001" />
<cffunction name="udf">
<cfargument name="udfname" type="string" required="true">
<cfif NOT isDefined(udfname)>
<cfinclude template='./udfs/#udfname#.cfm'>
</cfif>
<cfset tempfunc = variables[udfname]>
<cfreturn tempfunc(argumentCollection=arguments)>
</cffunction>
index.cfm
<cfoutput>
#udf(udfname='testUDF',firstname='John',lastname='Smith')#<br/>
#udf(udfname='testUDF',firstname='Betty',lastname='Ford')#<br/>
</cfoutput>
/udfs/testudf.cfm
<cfscript>
function testUDF() {
return 'Hello ' & arguments.firstname & ' #arguments.lastname#';
}
</cfscript>
I suspect something is up with relative paths.
Can you make "UDF" a mapping? Then you can do
<cfinclude template="/UDF/udf_library.cfm">