coldfusion problem about onRequest method in Application.cfc - coldfusion

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.

Related

Scope not working outside of the CFC folder for Session scope

I recently installed ColdFusion 2018 at work and have been frustrated by the inability to get scope working correctly. As usual I put all my .cfcs into the /CFC folder, and none of them will execute without a blank application.cfm file in that folder. I have tried extending application, including application, proxy extending application, moving the CFCs to the root folder only gets me syntax error on JSON. I have read every article that I can find for the past two weeks and I am still unable to understand why scope will not work. It seems I can set session variables within the /CFC folder, but they are not available outside the folder? I have not worked with CF for a few years, but consider myself versed, and for the life of me cannot get this working. its probable that I am missing the forest due to trees, but if anyone would be willing to assist, I would be grateful.
instantiated object;
application.SessionMgr = CreateObject(this.obj,'CFC.SessionMgr').init('session');
proxy call;
cfajaxproxy cfc="CFC/SessionMgr" jsclassname="SessionMgr";
return is correct;
var s = new SessionMgr();
var setReport = s.setValue('ReportID', document.getElementById('cboReportKey').value);
alert(setReport);
however even manually setting session.ReportID = 7 will not persist outside the folder.
here is the SessionMgr.init
this is the init;
<cffunction name="init" access="public" returntype="SessionMgr" output="no" hint="I instantiate and return this object.">
<cfargument name="scope" type="string" required="yes">
<cfargument name="requestvar" type="string" default="SessionInfo">
<cfset var scopes = "application,Client,Session">
<cfif Not ListFindNoCase(scopes, arguments.scope)>
<cfthrow message="The scope argument for SessionMgr must be a valid scope (#scopes#)." type="MethodErr">
</cfif>
<cfset variables.scope = arguments.scope>
<cfset variables.requestvar = arguments.requestvar>
<cfset updateRequestVar()>
<cfreturn this>
</cffunction>
and the setValue fn
<cffunction name="setValue" access="remote" hint="I set the value of the given user-specific variable." returntype="string">
<cfargument name="variablename" type="string" required="yes">
<cfargument name="value" type="any" required="yes">
<cfset var val = arguments.value />
<cfset SetVariable("#arguments.variablename#", val) />
<cfset r = Evaluate(arguments.variablename) />
<cfreturn r />
</cffunction>
ok, after trying everything, heres the solution. extending by proxy doesnt work for this situation, tried that. What finally worked was creating an application.cfc IN the /CFC folder and stripping out all functional components from the /root application.cfc and simply ensuring the application name was the same in the stripped down version in /CFC folder as the /root cfc name. This apparently psuedo extends all the functionality from the /root application.cfc and makes everything available to the framework in the /CFC folder. Thanks to everyone here helping to get me to think outside my wheelhouse and resolving this issue.

Any one using Mura, how can I safely add a globalFunctions.cfc

I am new to Mura and have a lot of existing code that I am trying to utilize. I have a globalFunction.cfc file that has a lot of functions that I need to have access to for the existing code. Previously I always extended my application.cfc to the global function so they where always there. With Mura I am not sure where to include it and still keep the installation "upgrade safe".
Any suggestions are appreciated.
In your [site]/includes folder is an Application.cfc. I believe that is the one you are looking to have extend your globalFunction.cfc. It is update safe.
Lance,
You can just put any functions you're wanting to use throughout your site in your eventHandler or contentRenderer files in your theme's folder. These are update safe, and depending on how you're wanting to use them, you can use one for display and the other for function.
EventHandler Ex:
<!--- PAGE - Default --->
<cffunction name="onPageDefaultBodyRender" output="true" returntype="any">
<cfargument name="$">
<cfif $.getcontentID() neq "00000000000000000000000000000000001">#$.dspInclude('/themes/MYTHEME/display_objects/bodies/dsp_body_default.cfm')#</cfif>
</cffunction>
ContentRenderer Ex:
<cffunction name="removeLinks" returntype="string" access="public">
<cfargument name="str" default="" required="true">
<cfset str=reReplace(str, "<[[:space:]]*[aA].*?>(.*?)<[[:space:]]*/[[:space:]]*a[[:space:]]*>","\1","all") />
<cfreturn trim(str) />
</cffunction>
The EventHandler here just puts out an different body if its on the home page, where the contentRenderer removes any links if i use $.removeLinks(MYURLSTRING).
HTH

Coldfusion: splitting a tag across onRequestStart() and onRequestEnd() in Application.cfc

I'm trying to see if there is a way to split a CFSAVECONTENT tag across the onRequestStart() and onRequestEnd() functions in Application.cfc to save the generated HTML of any .cfm page in the application to a variable.
Adding <cfsavecontent variable="html"> to onRequestStart() and adding </cfsavecontent> to onRequestEnd() isn't allowed since the tag must be closed in the function.
Is this even possible to do? I'm trying to avoid hard coding the CFSAVECONTENT this into every .cfm page of the site.
Thanks!
Alex,
You could do something like this in OnRequest (untested, but should work).
<cffunction name="onRequest" returnType="void">
<cfargument name="thePage" type="string" required="true">
<cfsavecontent variable="html">
<cfinclude template="#arguments.thePage#">
</cfsavecontent>
<!--- do whatever you want with the html variable here (for example, output it) --->
<cfoutput>#html#</cfoutput>
</cffunction>
I realize this has an accepted answer already, but another way to accomplish this without using cfinclude would be to use the getPageContext() object in onRequestEnd() to nab the generated content:
<cffunction name="onRequestEnd" output="yes">
<cfargument type="string" name="targetPage" required="true" />
<cfset var html = getPageContext().getOut().getString() />
<!--- Manipulate the html variable. --->
<cfoutput>#html#</cfoutput><cfabort />
</cffunction>
The <cfabort /> is important here because if you don't abort the request, the CF engine will output the generated content again and it will end up sending two copies of the output along.
I've used this method to apply site-wide changes to content on sites in a crunch where finding every instance of the original content wasn't practical or timely enough. It can also be used to send the generated content out to a translation service if needed before being returned to the end-user.

What is the proper way to assign a general udf to application.cfc?

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

extend Application.cfc, but not from the root

I have:
1. inetpub/wwwroot/ProjectName/Application.cfc
2. inetpub/wwwroot/ProjectName/Admin/Application.cfc
I want #2 to extend #1 and override the onRequest function.
I've looked into Sean Corfields's ApplicationProxy.cfc solution, but that is if your project is in the root folder, which mine isn't.
Can you create a mapping to the directory that contains App.cfc #1? If so, you may be able to extend "yourMappingName.application".
Both extends=".Application" and extends="/Application" should work if Application.cfc you need to extend is in the root.
In the root, create a file named AppProxy.cfc. Its contents are thus:
<cfcomponent output="false" extends="application" displayname="Application.cfc Proxy" hint="Extends the root application object so that subdirectories may extend it.">
</cfcomponent>
Then, in your subdirectory, set up your application.cfc to extend AppProxy.cfc. This will successfully inherit your root directory application.cfc methods.
<cfcomponent output="false" extends="AppProxy">
<cffunction name="onRequestStart" output="true">
<cfset super.onRequestStart() />
<!--- Some other stuff happens here. --->
</cffunction>
</cfcomponent>
This will work, by the way, even if the AppProxy isn't in the root directory. In that case, make sure your "child" application.cfc uses dot notation to find the AppProxy.
<cfcomponent output="false" extends="Path.To.Child.Directory.AppProxy">
<cffunction name="onRequestStart" output="true">
<cfset super.onRequestStart() />
<!--- Some other stuff happens here. --->
</cffunction>
</cfcomponent>
I use includes in onRequestStart and onApplicationStart.
That way when I am writing another Application.cfc, I can just include the code.