the Application.cfc's onMissingTemplate function is a great place to catch error if the user accesses a template in url which does not exists.
But as I have templates which dynamically include the other templates using cfinclude tag. But but if this dynamically generated path does not exist coldfusion does not calls the onMissingTemplate function.
Is there another way to do this?
you could catch the error with a cfcatch:
<cftry>
<cfset x = 'templateName'>
<cfinclude template="#x#.cfm">
<cfcatch type="MissingInclude">
//Do something
</cfcatch>
</cftry>
Related
I have a function called "conv" which is needed in several programs within my application. So rather than include it in each program, I put it in the Application.cfm, which looked like this:
<cfapplication name = "Moxware" sessionmanagement = "Yes">
<cfset lang = "LU">
<cfset x127 = Chr(127)>
<cfset mmox = 'Moxware'>
<cfinclude template="conv.cfc">
When I ran one of the programs using the function "conv" (which is in conv.cfc) I got an error that the function conv could not be found.
After I hunted around the internet for ideas I tried this:
<cfapplication name = "Moxware" sessionmanagement = "Yes">
<cfset lang = "LU">
<cfset x127 = Chr(127)>
<cfset mmox = 'Moxware'>
<cffunction name="onRequestStart" output="true" returntype="void">
<cfinclude template="conv.cfc">
</cffunction>
That gave me the same error message as before.
Can someone explain to me how to do this?
Note that the function conv was tested and works just fine.
Instead of using an include, like this...
<cfinclude template="conv.cfc">
Try creating an object, like this...
<cfscript>
MyObject = createObject("component", "conv");
</cfscript>
When you want to access a function within that object, try this...
<cfscript>
SomeValue = MyObject.MyFunction();
</cfscript>
You can include CFM pages into the CFC, but I don't think you can include CFC code into a CFC.
So this is possible...
<cffunction name="OnRequest" access="public" returntype="void" output="true" hint="Fires after pre page processing is complete.">
<cfargument name="TargetPage" type="string" required="true" />
<cfinclude template = "/myMapping/onRequestStart_include.cfm" />
<cfinclude template = "/myMapping/onRequest_include.cfm" />
<cfinclude template = "/myMapping/onRequestEnd_include.cfm" />
</cffunction>
Note the use of a mapping (in our case /myMapping), which can help if your doing this in CFC's. If no mapping is needed, just drop that.
But probably the best option is to instantiate your CFC from within the Application.cfc and use it.
<cfset myConv = createObject("component", "myMapping.conv").init() />
Again using a mapping to get to the CFC. The .init() is not always needed, depends how your CFC is setup.
Then presumably conv has methods you want to use (You talk about it as a single function? A cfc is essentially an object, so you create it as an object first and then use it's methods), so then you'd invoke then by using...
myConv.functionName()
Put another way - Application.cfc is no different from anywhere else in your code. How ever it's invoked and used elsewhere is how you should do it here. The only trick might be pathing to that CFC, which you can do by creating a custom mapping.
Also consider going old school (if it is just a function), and using custom-tags.
Good luck.
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">
If this has been asked before I apologize but I wasn't able to get a solid enough understanding without some assistance ....
I have a Local ColdFusion9 App that I use for development and testing running on WinXP SP3 with Apache 2 ...
What I'm having a problem understanding how to call request elements passed from the Application's 'THIS' scope to the onRequestStart() method.
For instance, My Application.cfc has the following in 'THIS' ...
<!--- MY 'THIS' Statements in Application.cfc --->
<cfscript>
/* define custom coldfusion mappings. Keys are mapping names, values are full paths */
this.mappings = structNew();
this.mappings['/tags'] = ExpandPath('/cfdev/tags');
</cfscript>
Further into my Application.cfc I have the following in the onRequestStart() function ...
<!--- Run before the request is processed --->
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfargument name="thePage" type="string" required="true">
<cfinclude template="#arguments.thePage#">
<!--- Lot's of onRequest statements and then ... --->
<cfset request.mappings = #THIS.mappings#>
<cfreturn true>
</cffunction>
Now ... Supposing I have a page where I call <cfdump var="#request.mappings#"> ...
I get an error stating 'Element MAPPINGS is undefined in REQUEST.' ...
However, (here's where my confusion begins) ... If I call <cfdump var="#request#"> Two structures are returned ... The first containing a key for "cfdumpinited" with a value of 'False' and the second with the structKey 'mappings' which contains another struct with a key of '/tags' and a value of 'C:\vir_dir\CFDEV\tags' as one would expect ...
If someone could please explain why it is that request succeeds yet request.mappings fails as I'm a bit in the woods here ... ;-)
Follow the logic through:
a request is made
onRequestStart runs
you explicitly include the template requested
youTHEN set request.mappings = this.mappings
onrequeststart finishes
the template requested executes
So if you have this in your requested template:
<cfdump var="#request.mappings#>
Then when you include that file at step 3... request.mappings doesn't exist yet (as they are created in step 4).
However when you change it to:
<cfdump var="#request#>
Then there's no error condition, so your CFINCLUDE runs fine, but DOESN'T OUTPUT ANYTHING (because you have output="false" on the function definition.
The dump you are seeing is the one coming from CF executing the requested template (step 6), by which time request.mappings exists.
I think you are confusing onRequestStart() with onRequest(), to be honest. It's in onRequest() that one might explicitly include the requested template, because onRequest() runs INSTEAD of CF running the requested template automatically. One would not generally include the requested template in onRequestStart() because it'll end up running twice.
Make sense?
I'm late to the party here, and may be missing a key element, but why not do this:
application.mappings = structNew()
instead of
this.mappings = structNew()
that way, you only set them once (on application start), you have access to them across your whole application, and you don't have the extra overhead of setting the request scope on every page hit.
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
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.