How to add path with dots in CreateObject Argument - coldfusion

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();

Related

Coldfusion Mappings For Different Locations For Different Applications

I currently have multiple applications on the same server. The applications are in different folders from each other.
In the application, we dynamically create a mapping called "custom", which needs to point to the appropriate "custom" folder for each application.
For example, take the following three applications and their paths:
app1 c:\inetpub\app1\custom
app2 c:\inetpub\app2\custom
app3 c:\inetpub\app3\custom
The code I use to do this (called in OnApplicationStart) for one application is:
<cffunction name="CreateAppMappings" output="no" returntype="void">
<cfargument name="absolutePath" required="yes" />
<cfscript>
mappingCustom = "/custom";
serviceFactory = createObject("java","coldfusion.server.ServiceFactory");
mappings = serviceFactory.runtimeService.getMappings();
mappings["/custom"] = "#arguments.absolutePath#\wwwroot";
</cfscript>
<cfreturn />
</cffunction>
The problem is, that for one application this works. But for multiple applications it is getting overwritten naturally, because the mapping name is the same.
In order for us to use the same codebase for all apps, this mapping name should be consistent ("custom") for each app.
So how do we accomplish this?
The only way I could think of is running each application as a seperate instance, using the instance manager in CFAdmin.
Is that my only option? Or is there something else I may not be aware of?
I am running CF2018.
Thanks
#Paolo Broccardo, As per #RRK suggestion you can set is as in your Application.cfc file like below
component output="false" {
this.name ='Your Apps name';
this.sessionManagement = true;
this.sessiontimeout = createTimeSpan(0,1,0,0);
this.root = getDirectoryFromPath( getCurrentTemplatePath() );
this.mappings[ '/custom' ] = "#this.root#/folder1" ;
.........
.........
}
Here the root I've point it as root directory for my application and /custom is for mapping for your custom directory details. You can set it like number things in app.cfc file itself.

Calling CFC's on another folder level

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.

Using <cfinclude> with this.mapping

In my application I have a header and footer include. In my Application.cfc I've set up a function that names my application and sets mapping.
<cfcomponent output="no">
<cfset this.name = "thesitename">
<cfset this.datasource = "thesitedatasource">
<cfset this.rootDir = getDirectoryFromPath(getCurrentTemplatePath()) />
<cfset this.mappings = structNew()>
<cfset this.mappings["/planning"] = "#this.rootDir#planning/" />
<cfset this.mappings["/images"] = "#this.rootDir#images/" />
<cfset this.mappings["/includes"] = "#this.rootDir#includes/" />
<cfset this.mappings["/js"] = "#this.rootDir#js/" />
<cfset this.mappings["/portfolio"] = "#this.rootDir#portfolio/" />
</cfcomponent>
If I have a page in a subdirectory like this: planning/index.cfm the <cfinclude> can't locate anything in the images folder when I use the following path: <li class="imagelink"><img src="/images/facebook.png"></li>
Pages in the root directory don't have a problem.
If I understand correctly, the problem has to do with the mapping doesn't take place prior to the include being called, or something like that... How do I get the mapped paths to work properly in my include?
ColdFusion mappings are completely separate from a web server 'alias' or 'virtual directory'. In order for your code to work, you will need to add a web server mapping, 'alias' in Apache or 'virtual directory' in IIS, named 'images' that points to the directory where you keep the images.
The 'images' ColdFusion mapping will only work in ColdFusion - for example, when creating an object, you could use createObject( "component", "images.image") (assuming of course that you had a CFC named Image in that diectory.

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