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.
Related
I'm trying to create Application specific mappings to a parent folder. For some reason, this works on my local dev environment, I get the error
invalid component definition, can't find component [cfc.udf]
Directory structure:
--- website1
------- application.cfc
--- cfc
------ udf.cfc
Code on application.cfc:
<cfcomponent>
<cfset This.name = "Website1">
<cfset This.Sessionmanagement=true>
<cfset This.Sessiontimeout="#createtimespan(0,0,30,0)#">
<cfset this.mappings["/cfc"] = expandPath('../cfc') />
<cffunction name="onApplicationStart">
<cfset Application.udf = createObject("cfc.udf")>
</cffunction>
</cfcomponent>
I'm using Lucee 1.5
When I output the application, like so:
<cfset app = CreateObject("component", "application") />
<cfdump var="#app#">
I get
MAPPINGS
Struct
cfc string D:\WebSites\parent-directory\cfc
I'm a bit lost
UPDATE:
in response to #leigh:
--- parent-directory
--- website1
--- application.cfc
--- cfc
--- udf.cfc
Turns out it had nothing to do with Coldfusion or Lucee.
The problem was how we had the sites set up on IIS. We had to switch from IP + : + port to using a subdomain for this environment. So, tomkat was looking in the wrong context... Or at least that's what I was told. This is a bit outside the layer of abstraction that I'm most comfortable with.
I'm in the process of switching over from using Application.cfm to Application.cfc and I'm using Ben Nadel's method for extending my application into a sub folder using an application proxy. (link to article)
The problem I'm having is that when I load a page in a sub folder all cfinclude tags that are called in the root Application.cfc file bring up a "Could not find the included template..." error message. (The include is intentionally at the top of the component so I can set application specific variables)
Here are a few requirements:
The application has to be run without access to the ColdFusion administrator.
The application may or may not reside in a sub folder of another site (i.e. www.example.com/ or localhost/mysite/)
Here's the file structure:
/application.cfc
/include_me.cfm
/index.cfm
/sub/application.cfc
/sub/application_rootProxy.cfc
/sub/index.cfm
Root Application.cfm:
<cfcomponent
output="false"
hint="I define the application settings and event handlers.">
<!--- Define the application settings. --->
<cfset this.name = "TestApplication" />
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 10, 0 ) />
<!---
Store the path of the current template. We want to see if
this shows up as the root template or the sub template.
--->
<cfset this.ROOT_currentTemplatePath = getCurrentTemplatePath() />
<!--- Set a variable to indicate that the included file hasn't been run yet --->
<cfset this.includedFile = "no" />
<!--- include the file --->
<cfinclude template="include_me.cfm" />
<cffunction
name="onApplicationStart"
access="public"
returntype="boolean"
output="false"
hint="I initialize the application.">
<!--- Set some app variables for testing. --->
<cfset application.ROOT_onApplicationStart = true />
<!--- Return true so the page can process. --->
<cfreturn true />
</cffunction>
<cffunction
name="onRequestStart"
access="public"
returntype="boolean"
output="false"
hint="I initialize the request.">
<!--- Set some request variables for testing. --->
<cfset request.ROOT_onRequestStart = true />
<!--- Return true so the page can process. --->
<cfreturn true />
</cffunction>
<cffunction
name="onRequest"
access="public"
returntype="void"
output="true"
hint="I process the user's request.">
<!--- Define arguments. --->
<cfargument name="script"type="string" required="true"hint="I am the request script." />
<!--- Output the current THIS collection. --->
<cfdump var="#this#" label="THIS" />
<!--- Include (execute) requested script. --->
<cfinclude template="#arguments.script#" />
<!--- Return out. --->
<cfreturn />
</cffunction>
</cfcomponent>
Root Include_me.cfm:
<!--- update the value so we know the file was indeed included --->
<cfset this.includedFile = "yes" />
Sub Folder Application.cfc
<!--- extends the application so we can make changes when needed --->
<cfcomponent extends="application_rootProxy">
<cfset this.SUB_currentTemplatePath = getCurrentTemplatePath() />
</cfcomponent>
Sub Folder Root Proxy:
<cfinclude template="../application.cfc">
What is the correct way to allow cfinclude tags in the base application.cfc when you're accessing the application through a root proxy?
My initial instinct was to see if I could calculate the application root dynamically and luckily getCurrentTemplatePath() is able to differentiate between the sub application.cfc and the root application.cfc. However cfincludes don't work when you try and access them via a local file system link (e.g. d:\mysite\include_me.cfm). It looks like I need to somehow figure out the dynamic relative position of the included file based on the sub directory of the executing application.cfc. Any and all help is appreciated!
I may be on to something... and if this is the answer hopefully it will help someone else out who finds themselves in a similar predicament.
I noticed that the cfinclude within the OnRequest() method processes normally regardless of whether the template is being called from the root of the application or a sub directory. Therefore I theorized if I put my cfincludes within methods they might execute properly.
So instead of placing my cfincludes at the top of my root component:
<cfcomponent>
<cfinclude="include_me.cfm">
...
</cfcomponent>
I can put them in a separate method and then call that method within the component:
<cfcomponent>
<!--- call the method which includes the file --->
<cfset includeFile() />
<!--- new method for including the file --->
<cffunction name="includeFile">
<cfinclude="include_me.cfm">
</cffunction>
...
</cfcomponent>
The key to this seems to be not including anything in application.cfc unless it's contained within a method.
I'm not sure what's causing what you're seeing, but you do have an unorthodox way of proxying your Application.cfc
This doesn't solve your issue, but here is a demonstration of doing the proxying correctly, and this doesn't have the include pathing issue you're seeing.
All the code is here: https://gist.github.com/daccfml/3ed091c62d688595d66e
/Application.cfc
component {
writeOutput("#getCurrentTemplatePath()# called<br>");
include "inc.cfm";
}
/inc.cfm
<cfoutput>#getCurrentTemplatePath()# called<br></cfoutput>
/ApplicationProxy.cfc
component extends="Application" {
writeOutput("#getCurrentTemplatePath()# called<br>");
}
/sub/Application.cfc
component extends="ApplicationProxy" {
writeOutput("#getCurrentTemplatePath()# called<br>");
}
/sub/test.cfm
<cfoutput>#getCurrentTemplatePath()# called<br></cfoutput>
This outputs:
C:\wwwroot\Application.cfc called
C:\wwwroot\inc.cfm called
C:\wwwroot\ApplicationProxy.cfc called
C:\wwwroot\sub\Application.cfc called
C:\wwwroot\sub\test.cfm called
Which is what I'd expect.
Rearrange your code to do the proxying correctly, and hopefully your issue will disappear. If not, update your question and we can revisit.
I'm moving away from ColdFusion 8 to ColdFusion 10.
Currently, on my Unix's root directory, I only have 1 Application.cfm and under this root directory I have about 10 sub directories (previous programmers did it this way and I'm experiencing a lot of weird things).
Now that I get the chance to redo this web application, I want to do it properly but the biggest problem with me is to properly understand how to work with Application.cfc in CF10.
Each of the sub directory represents a web application. For example, there is a web application for tracking graduate students, a web app. for tracking alumni, a web app. for formatting addresses, etc.
Users for these applications are from 10 different institutions. They all login to the application the same way (from the same interface) but then we separate them using session.usergoup & session.username to know who is who and who can see what type of things.
All Institutions share the same database, so currently only the datasource is set to application scope.
Unfortunately after reading many Application.cfc postings at this forum I got even more confused, so I hope you guys don't mind assisting me so that I can feel more comfortable working with Application.cfc in CF10.
My understanding is:
On my root dir I will create one main Application.cfc. My Main Application.cfc will only handle login/user authentication.
So under this root dir, I will have 1 Application.cfc, loginform.cfm and loginaction.cfm
In loginaction.cfm is where I set session.usergroup and session.username upon successful user authentication.
So in my main Application.cfc, I should set the following:
<cfset THIS.Name = "InstitutionMainApp" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan(0,0,30,0) />
<cfset THIS.SessionManagement = true />
<cfset THIS.SetClientCookies = false />
under OnApplicationStart I'll do:
<cfset application.dsn = "MyDB">
and under OnSessionStart I'll do:
<cfset session.usergroup= "">
<cfset session.username= "">
Then on each of my sub-folder's Application.cfc I need to name it differently
For graduate tracking system I should have:
<cfset THIS.Name = "GraduateTrackingSystem" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan(0,0,30,0) />
<cfset THIS.SessionManagement = true />
<cfset THIS.SetClientCookies = false />
under OnRequestStart, when I need to encrypt url variable (form example), I can set:
<cfset request.mySecretKey = application.mySecretKey />
<cfset request.algorithm = "AES" />
<cfset request.encoding = "hex" />
For alumni tracking system I should have:
<cfset THIS.Name = "AlumniTrackingSystem" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan(0,0,30,0) />
<cfset THIS.SessionManagement = true />
<cfset THIS.SetClientCookies = false />
under OnRequestStart, when I need to encrypt url variable (form example), I can set:
<cfset request.mySecretKey = application.mySecretKey />
<cfset request.algorithm = "AES" />
<cfset request.encoding = "hex" />
For Address Formating app., I should set:
<cfset THIS.Name = "AddressFormattingSystem" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan(0,0,30,0) />
<cfset THIS.SessionManagement = true />
<cfset THIS.SetClientCookies = false />
under OnRequestStart, when I need to encrypt url variable (form example), I can set:
<cfset request.mySecretKey = application.mySecretKey />
<cfset request.algorithm = "AES" />
<cfset request.encoding = "hex" />
Then since the application.dsn, session.usergroup and session.username are all set in
the Main Application.cfc under the root dir., I can freely use these scoped variables
on each application sub-folder safely because each of sub-folder application.cfc is
named differently this way I should not be concern with cross reference among usergroup
and username?
Please let me know if my understanding on how to use Application.cfc is very much a
mess.
That's a long question.
I think this is what you need: http://corfield.org/blog/index.cfm/do/blog.entry/entry/Extending_Your_Root_Applicationcfc
Once you have extended your application.cfc via some proxy to workaround the limitation of extending itself, you should be able to do almost anything your existing application.cfm was setup to do.
update: as iKnowKungFoo pointed out in the comment, once you have a different application name i.e. this.name you cannot share the vars in the Application scope since you're essentially forking a new application, you may try Server scope for those. Logic can be shared via functions you have inherited, but pay attention to whether the vars are accessible in your application.
I am trying to convert an application I support from application.cfm to application.cfc. I followed Ben Nadel's ColdFusion Application.cfc Tutorial And Application.cfc Reference, but my pages cannot access any of the variables in the APPLICATION scope, without adding APPLICATION. to the variable. As a side note, this application uses 0 cfc's currently.
here is my application.cfc.
<cfcomponent displayname="Application" hint="Handle the application" output="true">
<cfset THIS.Name = "AAS" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 60, 0 , 0) />
<cfset THIS.SessionManagement = true />
<cfset THIS.setClientCookies = false />
<cfset THIS.versionNum = '1'>
<cfset THIS.genericUserID = 'o005265'>
<cfset THIS.genericPassword = 'zo005265'>
<cffunction
name="OnApplicationStart"
hint="Fires when the application is first created."
access="public"
output="false"
returntype="boolean">
<cfset APPLICATION.aasURL = 'http://127.0.0.1:8500/aaas'>
<cfset APPLICATION.dsn = 've0_aaas'>
<cfset APPLICATION.aas_system = 'development (studio)'>
<cfreturn true />
</cffunction>
</cfcomponent>
Basically I just copied what was in the application.cfm page, and figured it would work the same. I am guessing that I have to invoke this somewhere? That is the only thing that I can think of. Any help would be great.
--- EDIT ---
I have added the OnRequest and OnApplicationStart methods after #EvikJames answer
<cffunction name="OnApplicationStart" access="public" returntype="boolean" output="false" hint="Fires when the application is first created.">
<!--- Return out. --->
<cfset APPLICATION.aasURL = 'http://127.0.0.1:8500/aaas'>
<cfset APPLICATION.datasource = 've0_aaas'>
<cfset APPLICATION.aas_system = 'development (studio)'>
<cfreturn true />
</cffunction>
<cffunction name="OnRequest" access="public" returntype="void" output="true" hint="Fires after pre page processing is complete.">
<!--- Define arguments. --->
<cfargument name="TargetPage" type="string" required="true">
<!--- Include the requested page. --->
<cfinclude template="#ARGUMENTS.TargetPage#" />
<cfset VARIABLES.dsn = APPLICATION.dsn>
<cfset VARIABLES.aasURL = APPLICATION.aasURL>
<cfset VARIABLES.aas_system = APPLICATION.aas_system>
<!--- Return out. --->
<cfreturn />
</cffunction>
You aren't really trying to use "application" variables (which always need to be scoped). I suspect your old application.cfm page had something like.
<cfapplicatin name="blah"...>
<cfset dsn = 'mydsn'/>
And then you were able to do:
<cfquery datasource="#dsn#">
This approach does not utilze the application scope ... it is merely taking advantage of the fact that your application.cfm always runs no matter what. What it is actually doing is putting variables in the variables scope. Because CF always checks the "variables" scope first you soemthing like #dsn# works - but that is not the same as the application scope.
To mimic this behavior in Application.cfc (as has been suggested) you will need to put your variable in the "onRequest()" function instead of the "onApplicationstart()" function - like so:
<cffunction name="onRequest">
<cfset dsn = 'mydsn'/>
</cffunction>
That is expected. To reference application variables you need to prefix them with application.
In your onApplication start method, do this:
<cfset APPLICATION.datasource = 'MyDSN'>
In your onRequest method, do this:
<cfset VARIABLES.DSN = APPLICATION.datasource>
Then, this query will work:
<cfquery datasource="#dsn#">
// sql
</cfquery>
I should add that when you are fully upgraded, you can remove all of the code above just set the default datasource:
<cfset THIS.datasource = 'MyDSN'>
If the variable is in the application scope, you will always need to identify it that way in your .cfm pages. If you use a variable name without the scope prefix, the application scope is not looked at.
if you want to declare variables in the application.cfc that can be accessed without the application scope in your other pages, simply declare the variables outside of any functions.
<component>
<cfset this.name = "applicationName">
<cfset otherVar = 'something'>
<cfset otherVar2 = 'something else'>
<cffunction name="onApplicationStart>.....</cffunction>
</component>
otherVar and otherVar2 can be called without scope prefix on all .cfm pages.
It sounds like you were not originally using application scoped variables. If the variables were not originally scope with "application." then they were simply in "variables scope" (confusing wording I know) which is accessible by the cfm page hit and others included. That is one big change when moving between application.cfm and application.cfc. The general idea there follows the principle that included CFM files share variables scope and CFC files do not.
If the only change you have to make is changing #dsn# to #appplication.dsn# then just do it and get it over with. There are tools such as WinGrep or even Notepad++ which have find and replace across multiple files.
Currently, in the application.cfc, I extend the Fusebox 5.5 Framework.
Then in the OnRequestStart method below, I set the fusebox mode depending on a certain condition.
The problem is that sometimes, the fusebox xml files do not reparse no matter what changes I make. If I force a reparse using the url variables fusebox.parse=true&fusebox.loadclean=true&fusebox.password=xxx then the files parse again.
It is almost like Fusebox remains in production mode even though when I dump the FUSEBOX_PARAMETERS.mode it says "development-full-load"
What could be causing this? Is the way that the fusebox mode is being manipulated correct in the code below or should that kind of setting be done somewhere else (besides the fusebox.xml obviously)??
Any help would be great.
Thanks
<cffunction name="onRequestStart">
<cfset variables.server_type = "Development" />
<cfswitch expression="#variables.server_type#">
<cfcase value="development">
<cfset FUSEBOX_PARAMETERS.mode = "development-circuit-load" />
<cfset FUSEBOX_PARAMETERS.debug = true />
<cfset request.component_reload = true />
</cfcase>
<cfdefaultcase>
<cfset FUSEBOX_PARAMETERS.mode = "production" />
<cfset FUSEBOX_PARAMETERS.debug = false />
<cfset request.component_reload = false />
</cfdefaultcase>
</cfswitch>
<cfif (StructKeyExists(attributes, "fusebox.loadapp") AND attributes.fusebox.password EQ application.fusebox.password) OR FUSEBOX_PARAMETERS.mode NEQ application.fusebox.mode>
<cfset this.onApplicationStart() />
</cfif>
<cfset superReturn = super.onRequestStart(arguments.1) />
</cffunction>
See, FUSEBOX_PARAMETERS are stored in application scope, by default they are included in huge container application.fusebox. Fusebox settings are populated when super.onApplicationStart() invoked, so modifying them in onRequestStart does not make sense.
I would recommend to move your cfswitch code into the component body where you define application settings.
In onRequestStart you can force the application restart to reread the settings, possibly something like this:
<cfif StructKeyExists(attributes, "fusebox.loadapp") AND attributes["fusebox.password"] EQ application.fusebox.password>
<cfset this.onApplicationStart() /
</cfif>
Please note that fusebox.loadapp is not built-in Fusebox attribute, it will work only for your app, simply prefixed like others for convenience. This way you can reread the singletones of your application.