I am needing to display some analytics regarding how many times a "OnRequestStart" function is called, or rather the number of hits per hour on our internal API that’s contained in a .cfc file. Preferably through the life cycle method; however the counter must continue outside of the life cycle. I know this can be done easily in other languages, but I am new to ColdFusion and have been trying to read through the documentation to see if there is some form of life cycle method I can use in order to achieve this. If there are any sort of documentation I am missing (I've tried learn cf in a week, cfdocs, adobe documentation), but there's not really much out there. This probably isn’t 100% clear, but if there is any clarification needed I’ll be happy to help.
Edit: I figured it would be best to set an application variable in onApplicationStart and incrementally add 1 to the counter variable within the onRequest start. Here is my example code:
Application.cfc:
<CFFUNCTION NAME="OnApplicationStart" ACCESS="PUBLIC" RETURNTYPE="BOOLEAN">
<cfset Application.timer EQ 0/>
<cfset Application.counter EQ 0/>
</CFFUNCTION>
somepage.cfm
<tr>
<cfoutput> #Application.counter#</cfoutput>
</tr>
I thought this would work, but I get an error saying Element COUNTER is undefined in APPLICATION. Is there something I am missing? I tried to restart the CF server service and web server, but no luck.
Thank you everyone for your help
Write it to the application scope, in onRequestStart(), include the following code:
lock scope="application" type="exclusive" timeout=1 throwontimeout=false {
if (!application.keyExists("reqCount") {
application.reqCount= 0;
}
application.reqCount++;
}
Then you can use it whereever you need it.
Turns out, the simplest way to do this is to simply create a variable either outside of the lifecycle or within onApplicationStart then increment the variable with each onRequestStart. You can then do whatever is needed after that. One thing I did stupidly was put <cfset Application.timer EQ 0/> <cfset Application.counter EQ 0/> after a <cfreturn> tag. Lesson learned, don't assume anything, research everything haha.
Thank you everyone
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
I have the following at the top of a page...
<cfif isdefined("session.loggedin") and session.loggedin eq true>
<cfinclude template="includeUserInfo.cfm">
<cfinclude template="includeSideBar.cfm">
<cfelse>
<cflocation url="../sign-in.cfm">
</cfif>
When I execute the page and session.loggedin DOES equal true it executes as expected and adds the included files.
If I hit the page without logging in - I'm not being redirected to the sign in page - I'm just getting a completely blank page - zero html in the view source.
Session management is enabled in my application.cfc file
I'm not sure if what I am doing is wrong - appreciate any advice.
OK - so (as is often the case) I had missed the underlying cause of the problem - and while focussing on the imagined problem with the CFIF statement itself, missed the small cfinvoke that was before it in the code. That was trying to execute a database lookup, but failing when a user was not logged in.
swapping these around and making sure the block above is before everything else on the page has done the trick. Go me. Thanks everyone for the input.
How can I check whether Accept-Language header is present in request?
I've tried this, but it produces an error
<cfset requestData = GetHttpRequestData() >
<cfif IsDefined("requestData.Headers['Accept-Language']")>
...
Sorry for probably lame question. That's my first day of ColdFusion coding.
isDefined() is very limited in it's usage, so you can't use associative array notation with it. In almost every case you're better off using StructKeyExists() anyway. So, to solve your problem:
use:
<cfif StructKeyExists( RequestData.Headers, "Accept-Language" )>
I am trying to store coldfusion code in a database to be used for the subject of a cfmail. The code stored is as follows:
"RE: <cfif myData.general.legalName NEQ """"> {{dotlegalname}}<cfelse>{{docketLegalName}}</cfif>,
DOT## {{dot}}, Docket ##(s) {{docketString}}"
When I retrieve string from the database, I use cfsavecontent to attempt to evaluate it.
<cfsavecontent variable="subject">
<cfoutput>#myData.email.subject#</cfoutput>
</cfsavecontent>
I also tried
<cfsavecontent variable="subject">
<cfoutput>#evaluate(myData.email.subject)#</cfoutput>
</cfsavecontent>
And then I replace all the {{ }} with the appropriate values.
However, the subject of the email is stubbornly refusing to contain an evaluated cfif, and is instead showing the cfif as if it were a string.
Any ideas?
The only way to dynamically evaluate code that you are creating at runtime is via writing it out to a file, and then executing it.
The easiest way would be to write it a .cfm page in the Virtual File System (probably name the file after a UUID, so it's unique), and then it where you need to run the contents.
I wouldn't normally advocate generating code at runtime like this, but it can be the most elegant solution in some cases.
As an alternative, instead of storing the CFML code in the database, you have a set of CFML email template files that get stored in a directory on your server, and in your database you simply record which template needs to be included either via cfinclude or cfmodule.
You can't dynamically evaluate CFML stored in a database without first writing it to file and then using <cfinclude> to include it.
Further to Mark's answer here is some psuedo code:
<cfset fileName = createUUID() & ".cfm">
<cfset fileWrite( fileName, [CODE_FROM_DB]>
<cfinclude template="#fileName#">
<cfset fileDelete( fileName )>
I have used code like this before with no problems. Anything in the Virtual File System flies as it is all run in RAM. For best practice do remember to delete the files created ;)
If you absolutely have to do this, look at the evaluate() function. This, essentially, fires up a new CF thread, compiles the string passed to it, runs it, and returns the result.
If at all possible, I would try to find a way to move your logic to the actual file being run, not the string from the database. I assume you are pulling the data based on some string you've already built, so you might consider appending something to it, so you are looking up subjectDotLegal and subjectDocketLegal or something similar.
Remember, evaluate() is slow, ugly, and can be dangerous (it will run anything passed to it!). If there's a way around it, I suggest you use it.
why not just use something like mustache?
http://mustache.github.com/
https://github.com/pmcelhaney/Mustache.cfc
it has the ability to not only do some of the logic that you want in your script dynamically. i really would suggest you check out the project and maybe even improve and contribute on it.
OH and just for the chance to be on a soapbox: I've been emailing Adobe for years saying that we need the ability to dynamically parse and render CFML. Sadly my cries have only gotten ignored. maybe if more people complained that this feature needs to be added, it would get the attention it deserves.
To give an example: Assume code.txt is a text file that contains the following (just to facilitate simulating CFML stored in a db): <cfoutput>#now()#</cfoutput>
The following code would work:
<cfset q = queryNew("code") />
<cfset queryAddRow(q,1) />
<cfset querySetCell(q, "code", fileRead(expandPath('code.txt')), 1) />
<cfdump var="#q#">
<cfset newCodeFile = expandPath('dynamic.cfm') />
<cfset fileWrite(newCodeFile, q.code[1]) />
<cfinclude template="dynamic.cfm" />
In OpenBlueDragon there is the render function, which can do this.
You can mimic this function in Railo by creating a custom built-in function that saves the file into RAM then cfincludes it, using the following code:
<cffunction name="render" output="Yes" returntype="string"><!---
---><cfargument name="Code" required="Yes" type="string"><!---
---><cfset local.mapping = {'/render_ram_resource':'ram://'}><!---
---><cfapplication action="update" mappings="#local.mapping#"><!---
---><cfset local.fileName = "/render_ram_resource/_render_" &
createUUID() & ".cfm"><!---
---><cffile action="WRITE" file="#fileName#"
output="#arguments.Code#"><!---
---><cfinclude template="#fileName#"><!---
---><cffile action="DELETE" file="#fileName#"><!---
---></cffunction>
(This looks unusual because it needs to allow output, but prevent extra whitespace, hence why all the comments. Unfortunately SO's syntax highlighting seems to be confused by them.)
If you need an ACF-compatible solution, you'll need to use the regular filesystem and a pre-created mapping. (Well, in ACF9 and above you can use the RAM virtual filesystem, but afaik you can't create mappings on the fly like this.)
There's a better way, namely using in memory files. This way you don't have any I/O on the disk and therefore much faster:
For tags that take logical path, define mapping in Administrator. Execute in-memory CFM pages using the cfinclude tag:
Create a mapping for ram:/// so that it can be used in the tags. In this example, /inmemory is the mapping that points to ram:///.
For tags that take absolute path, specify the syntax as provided in the following example:
You can also delete the file from the ram usinf cffile and action delete.
Here's how I stored my header and footers for all pages in a record. This code can go at the top of each page. But I have it in the APPLICATION.cfm and it seems to be working great.
The key here is not use #pound# signs on your expressions. User [square braces]. The code will pick them and evaluate them and return the result back to the template.
It will substitute the number 0 if it can not evaluate an expression as a means of error handling.
<CFSET FooterID=1234> <!-- ID of the record you want to use -->
<CFQUERY NAME="StoredHeader" Datasource="DS1">
Select Body from templates where id=#FooterID#
</CFQUERY>
<CFSET Parse=StoredHeader.Body>
<CFLOOP CONDITION="FindNoCase('[',Parse,1) GT 0">
<CFSET STB=FindNoCase('[',Parse,1)>
<CFSET ENB=FindNoCase(']',Parse,1)>
<CFIF ENB-STB GT 0>
<CFSET BracketExp=Mid(Parse,STB+1,ENB-1-STB)>
<CFTRY>
<CFSET BracketValue=Evaluate(BracketExp)>
<CFSET Parse=ReplaceNoCase(Parse,'['&BracketExp&']',Evaluate(#BracketExp#))>
<cfcatch type="any">
<div>'Using ZERO 0 for missing <cfoutput>#BracketExp#' </cfoutput> </div>
<CFSET Parse=ReplaceNoCase(Parse,'['&BracketExp&']','0')>
</cfcatch>
</CFTRY>
</CFIF>
</CFLOOP>
<CFSET Footer=Parse>
<cfoutput>FOOTER</cfoutput>
I would try the built-in QuoteName function.