I recently took over a site that is a complete and utter disaster. Very bad code and broken every time I turn around. My question is, with ColdFusion, how can I show/display every page that is included in a page. This site has includes within includes within includes. Naming conventions are terrible, with thousands of lines of code in each. No variables passed in page urls...I mean seriously, I can go on and on. I just need to know all the pages that are included when a specific page loads so I know where to look for the problem.
Check out the Enable Request Debugging Output in your ColdFusion Administrator. This settings is located in the Debug Output Settings section. Then turn on Report Execution Times. This will output all the files used to build the page and the time each template took at the bottom of your page.
More information from the documentation:
CF10 Debug Settings Documentation
CF9 Debug Settings Documentation
I found a related comment on Ben Nadel's blog from Roger Tubby in 2011:
http://www.bennadel.com/blog/116-finding-template-execution-stack-in-coldfusion.htm#comments_36939
This method doesn't require enabling debugging or configuring any IP address. Calling the java.lang.Exception java class will return an object with a TagContext key that contains an array of structs (in reverse order) that will identify template files & lines of execution, but not execution times.
<cfscript>
JavaException = createObject("java","java.lang.Exception").init();
writedump(JavaException.TagContext);
</cfscript>
Here's a screenshot of a sample dump of the TagContext array.
To expand on others' answers, the following code will dump out included templates, and who included them.
For this to work, you need to check Enable Request Debugging Output in CFAdmin > Debug Output Settings
<cfobject action="CREATE" type="JAVA" class="coldfusion.server.ServiceFactory" name="factory">
<cfset cfdebugger = factory.getDebuggingService()>
<cfset qEvents = cfdebugger.getDebugger().getData()>
<cfquery dbType="query" name="cfdebug_templates" debug="false">
SELECT template, parent, Sum(endTime - StartTime) AS et
FROM qEvents
WHERE type = 'Template'
GROUP BY template, parent
ORDER BY et DESC
</cfquery>
<cfdump var="#cfdebug_templates#">
Related
I am migrating a very old app (currently running in CF8) to Lucee. But I am running into a problem with what appears to be a custom tag of some sort.
I have tried to make sure that all the virtual directories are the same in IIS for both the old and the new installs. And made sure the mapping and custom tag paths in both the CFIDE and the Lucee Admin are the same.
But I am getting this error. And can't figure out how this cflink is being instantiated.
I have found the location of the erroring code on line 300 the utils.cfc file
I haven't used custom tags in a long time but thought they were generally called with an underscore and the code should like more like <cf_link pageid="#LinkPageID#" Init="start"> if this was being called as a custom tag.
If I go the the current CF server that is running this app I can find that a cfclass files HAS been created
From a file called cflink.cfm in a directory called "tags" even though there seems to be no mapping for the "tags" directory nor is is listed under "custom tags paths" in the administrator.
This App was start in 2003 and as you can imagine has grown into a mis-match of spaghetti code and no one from the beginning is around to ask how this tag is instantiated.
Does anyone with experience in legacy code has any other ideas where I should be looking to try to get this to work? The currently has only a production environment and if I can get it to work on Lucee it will not only be a dev environment that hasn't existed here in 10 years but will be a great way for me to be able to continue showcasing Lucee as a great CFML engine
Adding addition info
Leigh had asked if the init might be a jar reference but in the cflink.cfm file I see this code:
<cfif Attributes.Init IS "start">
<cfset Request.PageID = Attributes.PageID>
<cfset Request.Page_Width = Variables.qParentInfo.Page_Width>
<cfset Request.Page_Height = Variables.qParentInfo.Page_Height>
<cfset Request.Page_TypeID = Variables.qParentInfo.Page_TypeID>
<cfset Request.AddPath = "">
<cfif IsDefined("Attributes.Anchor")>
<cfset Request.Anchor = Attributes.Anchor>
<cfelse>
<cfset Request.Anchor = "">
</cfif>
<cfset Request.IsInternalLink = false>
<cfexit method="EXITTAG">
</cfif>
There are also references to cflink in the code inside tags\cflink.cfm
<cfif Len(Variables.qParentInfo.ParentID) GT 0>
<!--- Add the page title to the end of the path --->
<cfset Request.AddPath = ReplaceNoCase(Variables.qParentInfo.Nav_Title," ","_","ALL") & "/" & Request.AddPath>
<cflink init="working" pageid="#Variables.qParentInfo.ParentID#" popcode="#Attributes.popcode#">
<cfelse> ......</cfif>
Although this may be recursion given it was written in 2004 I kind of doubt it
Adding screen shots of searches
If anyone else runs into this. In CF8, and presuming earlier versions, you could put a cfm file into the ColdFusion8\wwwroot\WEB-INF\cftags Directory and that file in this case ColdFusion8\wwwroot\WEB-INF\cftags\link.cfm Then acts as any other cftag.
I was able to find the person who originally build this app in 2004 and he told me that they did it this way to avoid typing the underscore that they would have typed if they'd done it as a custom tag.
I kind of get it since this tag is used everywhere in the app, literally hundreds of times. Bit boy with a bitch to find.
Now all I have to do is figure out how to move it to the Lucee world in a similar fashion. So it instanciates the same way.
Thanks #Leigh for all your help, you are always amazing!
Adding more information
if there are files in the WEB-INF\lucee\library\tag the corresponding Lucee directory is WEB-INF\lucee\library\tag. These files are read on load and then able to be used as any other cf tag.
For example if you have file WEB-INF\lucee\library\tag\link.cfm it can be called by `cflink'.
Seems like a cool idea but a bit of a bitch for someone to find 10 years after the fact
Within my root application.cfc I define this.customTagPaths. This is verified to work on all pages, including those in subfolders. Within one subfolder I have an application.cfc that extends this root application.cfc. Pages within this folder still use the correct custom tags so we know that this is working correctly.
However, when trying to use a custom tag within the [subfolder]/application.cfc file itself I believe it is pulling from a different custom tag path. I added some debug information into the custom tag and it outputs when the custom tag is called from a normal page, but does not output when called from the application.cfc. I do not have access to the server to put debugging information in the other custom tag paths to be sure.
Does code in the application.cfc ignore this.customTagPaths and, if so, how do I use the specific tag I need? This custom tag sets a variable in the caller scope, so it cannot be called with a simple cfInclude.
Edit
I will attempt to address some of the questions in the comments here.
The custom tag in question has been simplified down to this code:
<cfset Caller.groupList = "">
<cfquery name="getGroups">
SELECT id, name
FROM groups
WHERE id = 1
</cfquery>
<cfoutput query="getGroups">
<cfset Caller.groupList = #ListAppend(Caller.groupList, name)#>
</cfoutput>
<cfoutput>Caller.groupList: #Caller.groupList#<br></cfoutput>
The Application.cfc is using this code:
<cfcomponent extends="RootApplication">
............
<cf_groupList>
<cfoutput>request.groupList: #request.groupList#<br><br></cfoutput>
</cfcomponent>
When cf_groupList is called directly from a cfm it writes "Call.groupList: xxxx" out to the page and shows the correct values from the dev database. However, when the Application.cfc runs the custom tag "Call.groupList: xxxx" never appears, but "request.groupList: xxxx" does, and in the latter case it shows the list that we would expect from the live database. Both the live and dev sites are currently on the same server, which we are in the process of changing, but for now I have no debugging information.
The reason I am calling a custom tag from Application.cfc is because this tag is used many other places. Simply copying and pasting the code into Application.cfc would solve the problem, but then we have an issue of duplicated code that we need to remember to update in two places in the future. Using the custom tag in the application.cfc instead of duplicating code seemed like the correct approach.
Mark, you are correct. When placed in the parent Application.cfc the custom tag works correctly. In the child it does not.
When you add a debug IP to review the debug information from the collection of templates that are parsed to present the page, it includes a list of all queries from that page.
Is it possible to get access to that object for inspection?
I'm looking at trying to automate the documentation which data sources are in use for which page requests. We have a large number of small web apps that access different databases and have different dependencies. I'm hoping to find a way to automate the documentation of these dependencies rather than having to manually review all code for all of the webapps.
Not sure if the object doesn't get created until after the page creation is too far gone to actually do anything with the data, but who knows...
Here is a snippet of code that you can add to the end of your template to get a list of datasources used on the page:
<cfobject action="CREATE" type="JAVA" class="coldfusion.server.ServiceFactory" name="factory">
<cfset cfdebugger = factory.getDebuggingService()>
<cfset qEvents = cfdebugger.getDebugger().getData()>
<cftry>
<cfquery dbtype="query" name="cfdebug_qryDSN">
SELECT DISTINCT DATASOURCE FROM qEvents WHERE type = 'SqlQuery'
</cfquery>
<cfcatch type="Any">
<cfset cfdebug_qryDSN = queryNew('DATASOURCE')>
</cfcatch>
</cftry>
<cfdump var="#cfdebug_qryDSN#" label="cfdebug_qryDSN">
PS: most of the inspiration for this snippet came from {cfusion 10 home}\cfusion\wwwroot\WEB-INF\debug\classic.cfm. You can get some good ideas on how to gain access to debugger objects/data from this file.
For anyone stumbling across this....
If your [cfroot]/cfusion/lib/neo-datasource.xml file is WDDX encoded and you're not sandboxed, you can use the following (tested on CF2021)
<cflock type="readonly" scope="Server" timeout="5">
<CFSET LibPath=Server.System.Properties["coldfusion.libPath"]>
</cflock>
<CFFILE action="Read" file="#LibPath#/neo-datasource.xml" variable="DatasourcesWDDX">
<cfwddx action="wddx2cfml" input="#DatasourcesWDDX#" output="Datasources">
<cfoutput>#StructKeyList(Datasources[1])#</cfoutput>
<cfdump var=#Datasources#>
The first position of the Datasources array holds a structure containing information on each configured datasource with the main key being the name of the datasource.
Here's an idea that'll work for each application which uses an Application.cfc.
Enable Request Debugging Output in CF Administrator.
Configure Debugging IP Addresses so that every page receives debugging information.
Assuming that Select Debugging Output Format is set to classic.cfm, short circuit {cfusion 10 home}\cfusion\wwwroot\WEB-INF\debug\classic.cfm by making <cfreturn> the first executable statement in classic.cfm. This will prevent any pages from seeing the debug output.
In Application.cfc::OnRequestEnd() do what Scott Jibben suggested. You can wrap Scott's idea in an <cfif IsDebugMode()>.
Using: CF10 and IIS7.5
I have a section within my website called "Bookings". It is located like this:
c:\inetpub\wwwroot\mysite\bookings
Within this folder will be sub-folders and eventually webpages themselves. Heres an example:
c:\inetpub\wwwroot\mysite\bookings\holidays\new.cfm
c:\inetpub\wwwroot\mysite\bookings\carhire\edit.cfm
I include (using <cfinclude>) another page within each webpage that displays different links depending on which page is calling it. All I want to know is the directory up to the "bookings" folder. Something like this (pseudo code):
<cfset whereAmI = #GetDirectoryFromPath(GetBaseTemplatePath())#>
<cfif #whereAmI# EQ "C:\inetpub\wwwroot\mysite\bookings">
<h1>Booking Section Links</h1>
</cfif>
The above code works only if the user visits the bookings/index.cfm page of the "bookings" folder. But if they go to the bookings/holidays/new.cfm page, it is now in the holidays folder so the <h1> content will not appear. I really only want to check for any page that is in the bookings folder, even if it is within a subfolder within the bookings folder. A bit like in SQL where I could say IF #GetDirectoryFromPath(GetBaseTemplatePath())# LIKE 'c:\inetpub\wwwroot\mysite\bookings%' so it has a wildcard on the end.
I know this question is going to irritate the MVC framework advocates but please excuse me on this!
Here is a quick, easy way to solve your problem (may not work as system expands - but should get you started down the right path).
<cfset whereAmI = GetDirectoryFromPath(GetBaseTemplatePath())>
<cfif whereAmI CONTAINS "C:\inetpub\wwwroot\mysite\bookings">
<h1>Booking Section Links</h1>
</cfif>
Note, I removed the # from inside the cfset and cfif you do not need them there.
You could even scale back the path to use just 'mysite\bookings'.
Ideally, this should be wrapped up into a function so that you can easily pass different paths into it to determine if you are on a given page. Or, possibly, even determine the 'parent' folder in onRequestStart in Application.cfc and set it as a request scope variable.
This will need to be tweaked if you run the code on a *nix based system.
It is more easier with CGI variables. You can use "CF_TEMPLATE_PATH". Try this
<cfoutput>The value of CF_TEMPLATE_PATH is: </cfoutput><cfdump var="#CF_TEMPLATE_PATH#">
In the application I designed, I named one of my web pages "error.cfm". I want it to display whenever there is an error from the application. So I put the following code inside "error.cfm":
An uncaught exception just 'happened' :-(
<br><br>
<b><cfoutput>#exception.message#</cfoutput></b><br />
<cfoutput>#exception.detail#</cfoutput><br /><br />
<cfif isdefined('exception.cause')>
<b><cfoutput>#exception.cause.message#</cfoutput></b><br />
<cfoutput>#exception.cause.detail#</cfoutput>
</cfif>
<cfdump var="#exception#">
So after hosting the website, I discovered that this particular page refused to load and instead a '500 Internal Error' was displayed. I then complained to my hosting company and I was sent these details:
Dear Customer,
The actual error message is the following:
Security: The requested template has been denied access to createobject(java).
The following is the internal exception message: access denied (coldfusion.runtime.FunctionPermission createobject(java))
The error occurred in C:\inetpub\vhosts\plat4ad.com\httpdocs\cms\error.cfm: line 10
8 :
9 :
10 :
Unfortunately some tags and functions are disabled on our servers due to security purposes. You can check full list here:
https://support.dailyrazor.com/index.php?_m=knowledgebase&_a=viewarticle&kbarticleid=293&nav=0,29,76
Please let us know if you have any other questions.
Best wishes,
Support-GG
DailyRazor Support Team.
Now checking the lists of the tags they disabled on their servers, CFDUMP was among them:
On the shared ColdFusion servers you will have access to all tags and
functions except for the following:
CF Tags:
CFCOLLECTION
CFCONTENT
CFDUMP
CFEXECUTE
CFLOG
CFOBJECT
CFOBJECTCACHE
CFREGISTRY
CF Functions:
SetProfileString
CreateObject(COM)
CreateObject(CORBA)
CreateObject(JAVA)
Please is there any alternative for CFDUMP? Or does anyone know any ColdFusion hosting company that doesn't have these restrictions? I appreciate any ideas from you.
How about this: http://www.coldfusionjedi.com/index.cfm/2010/10/6/Cant-dump-Try-a-fake-dump
If you get yourself VPS (google:coldfusion VPS) you'll be able to do on that machine whatever you want.
The whole point of not having cdump is security measure. Usually all debugging and dumping raw data is done on development server and on hosted server you run applications, right? Error.cfm is used to hide caught exception details, like path to your files, data about used libraries etc.
There're ways to display data without cfdump. In your case I don't see why you couldn't use plain cfoutput and write exception details as 2 lines of output.
In your place, I'd take source codes of fw/1 or Mura CMS to see how they handle error messages, better to see how more experienced people are doing it then to "waste" time reinventing the wheel.
CFDUMP began life as a custom tag before it was ever included in ColdFusion.
It still appears to be downloadable: http://www.adobe.com/cfusion/exchange/index.cfm?event=extensionDetail&extid=1002037
You may need to make changes to it to work (better) in more modern versions of CF, but it should probably do more than 90 per cent of what you need it for.
I will add that if you choose to "dump" details, you can also wrap the dump code in an "if" block that looks at your cgi.REMOTE_ADDR and if it matches your IP address, does the dumpout, otherwise doesn't.
<cfif cgi.REMOTE_ADDR EQ "167.96.177.66">
<!--- execute dump code here --->
</cfif>
Caveats: YMMV, have to have a static IP, have to BE at that IP, etc.