ColdFusion Limit to 5 E-mails a Day - coldfusion

Say I have the following script on page.cfm. Everytime somebody comes to page.cfm, an e-mail is sent to me. So if 1000 people come to page.cfm, I get 1000 e-mails.
<cfmail to="joe#smith.com"
from="error#smith.com"
subject="Error"
type="text">
A person visited.
</cfmail>
I would like to limit it so I only get e-mails when the first 5 people visit in one day.
So if 2 people visit today, I get 2 e-mails. if 5 people visit I get 5, but if 100 people visit, I still get only 5 e-mails. And then the cycle continues the next day (if 2 people visit only 2 e-mails are send, but if 100 people visit only 5 e-mails are sent)
How can I do this with ColdFusion only? (without cfschedule)

The simplest way I can think of is to put a counter and a date stamp in a pair of Application variables.
Then, for each request, check the date you've recorded. If it's not today, reset the counter variable to 1 and re-set the date to today.
Then, where you're putting the cfmail tag, do a check to see if the counter has reached your limit. If not, send the mail. Either way, increment the counter.
In Application.cfc onApplicationStart():
<cfset application.alertDate = now()>
<cfset application.alertCount = 1>
(the above is mostly to ensure that the variables exist when used later)
In Application.cfc onRequestStart():
<cfif dateCompare(application.alertDate,now(),"d") EQ -1)>
<cfset application.alertCount = 1>
<cfset application.alertDate = now()>
</cfif>
In page.cfm:
<cfset application.alertCount++>
<cfif application.alertCount LTE 5>
<cfmail ... >
</cfif>

Store the visit/email count in the Application scope, and reset the application scope every day by either
store the last visitor date in the application scope as well, and
add some logic to reset the count, or
use cfscheduler to reset the count to 0 every day

Late to the party, and I can't comment on previous answers just yet, so I'll do it this way. Both of the answers given will work, but you do need to know that those Application scope variables will disappear and reinitialize should your application end. If it's important to you, you will want to persist the values somewhere, something like an INI file accessed with getProfileString/setProfileString would work, or a table in a database.

Related

cflock/cfthread for auction closures

I'm building an auction site and I want to make sure that there are no issues with mutiple auctions closing at similar times. I have a basic understand of cflock and was wondering how this, or others like cfthread, could be applied optimally to protect this process from race conditions.
So far, my processing goes like this:
When an auction reaches its closing time, I initially set the status of "1" to indicate that the auction is closing. Site users then see the message "Closing" while the closure process takes place.
close_auction.cfm
<cfset resUpdateStatus = oListing.updateStatus(listing_id=url.listing_id, status=1)>
Then the call to the closeAuction() function:
<cfset resCloseAuction = oListing.closeAuction(listing_id=url.listing_id)>
listing.cfc
The closeAuction() function, with a cflock I'm currently using:
<cflock timeout="30" name="closeAuction_#arguments.listing_id#" type="exclusive">
<cftransaction>
<!--- if for whatever reason auction closure time not yet reached, delay a few seconds --->
<cfloop condition = "now() lt qListing.end_date">
<cfset oSystem.pause(TimeDelay=5)>
</cfloop>
<!--- processing here --->
- select on listing table to ensure listing still exists
- select on bids table - check high bid, determine if winner
- update listing table - status, winner_id, close_date etc
- update user_subscription table
- delete scheduled task that auto-closes the auction
- update tracking table
- delete listing from users' watchlists
</cftransaction>
</cflock>
This works fine while testing single auctions, but I'm concerned about what could happen under heavy load with many auctions closing potentially within seconds of each other. Any advice bullet-proofing this would be appreciated.
I'm on CF12
UPDATE
Updated to show processing inside cftransaction

Hiding (not eliminating) duplicates in ColdFusion array

We have a three-panel display in which Panel One shows a list of countries, Panel Two shows a list of organizations, and Panel Three shows a list of aircraft. If you click a country, both the organizations and aircraft are filtered by that country, using IDs for each.
This is all working fine, but the list of aircraft comes from a "linking table" -- each record contains aircraft ID and name, and operator ID and name. Any given type of aircraft can be listed multiple times, depending on how many organizations operate it. For example, "AW159 Wildcat" is listed twice, once for Army Air Corps and once for Royal Navy. The way it works right now, "AW159" is listed in Panel Three twice when the page initially loads.
I would like any given aircraft to be displayed only once on initial page load, regardless of how many times it may be listed in this linking table. But, I want all the "duplicates" available to be shown, if somebody filters on the operating organization.
Is there a way to loop over all the aircraft but somehow hide duplicates of aircraft ID? Or, might there be a larger problem with the way our database is designed? I am open to suggestions. Thank you all very much in advance!
EDIT: sorry for not posting code; here is some. Thanks everybody for all of your help!
1. Query and array population in cfc:
<cfquery name="queryByOperator" datasource="vertipedia">
SELECT view_aircraftbyoperators.aircraftID
, view_aircraftbyoperators.Aircraft
, view_aircraftbyoperators.operatorID
, view_aircraftbyoperators.Operator
, tbl_companies.country
, tbl_companies.companyShortName
FROM view_aircraftbyoperators JOIN tbl_companies
ON tbl_companies.ID = view_aircraftbyoperators.operatorID
ORDER BY Aircraft
</cfquery>
<cfset count = 1>
<cfset aircraftByOperator = arrayNew(2)>
<cfloop query="queryByOperator">
<cfset aircraftByOperator[count][1] = aircraftID>
<cfset aircraftByOperator[count][2] = Aircraft>
<cfset aircraftByOperator[count][3] = operatorID>
<cfset aircraftByOperator[count][4] = Operator>
<cfset aircraftByOperator[count][5] = country>
<cfset count = count+1>
</cfloop>
2. Outputting on page:
<ul>
<cfloop from="1" to="#ArrayLen(aircraftByOperator)#" index="i">
<cfoutput>
<li name="#aircraftByOperator[i][3]#" class="#aircraftByOperator[i][5]#">
<p>
#aircraftByOperator[i][2]#
</p>
</li>
</cfoutput>
</cfloop>
</ul>
[i][1], [i][3] and [i][5] are all IDs. "operatorID" is the one that will change depending on who operates the aircraft. I hope this helps explain what I am trying, and again, many thanks!
Dan Bracuk's comment "Does this mean you are pre-loading all your data into javascript? It might be more efficient to throw some ajax in there and just get what you need when you need it" ended up being the way to go for us. I would like to mark his comment as the answer, but am unable to see how to do that.
For others who may read this post, we
Loaded all of the aircraft initially
When a visitor filters by country and/or organization, a new query is sent via ajax
I was having a hard time formatting the returned data, so I used jQuery's load() to format it and drop it in the proper place in the output panel.
As Dan said, it is very fast and much easier to work with, especially as our database continues to grow.
Thanks Dan!

Using Memcached with Coldfusion

I am trying to use memcached for the first time but I have a few questions when it comes to queries with dynamic variables.
This is a sample of my code so far. I got everything working but if you see any mistakes on my code please let me know.
<cfoutput>
<cfset variables.memcachedFactory = createObject("component","memcachedfactory").init("127.0.0.1:11211")>
<cfset variables.memcached = variables.memcachedFactory.getmemcached()>
<cfset email = "sbcglobal"> //Pretend that this is a list with multiple choices for the user to choose from.
//The query below would have multiple outputs based on the selection of the user above
<cfquery name="test" datasource="#ds#">
select top 10 * from contact where email like '%#email#%'
</cfquery>
//Setting the key
<cfset set100 = variables.memcached.set(key="queryTest2",value=test,expiry="5000")>
//Getting the key
<Cfset test6 = variables.memcached.get("queryTest2")>
<br><br>this is the query test - did we retrieve it correctly? - <br />
<Cfdump var="#Test6#"><br />
</cfoutput>
Now, do i have to set a key for every possible outcome of the query? (which in my case would be a few hundred keys to cover all possible outcomes)
My understanding is that memcached saves the value to a key after the first request and after that you get the output without db calls.
If I use memcached in my case would it just be a waste of code since each query would be different based on the user's choice?
Also, how do you update the values in cache in case the output changes? Is this something that I would have to do manually? My database get 100's of transactions daily, a select * from contacts at 10 am will not have the same output at 5pm
The idea behind caching (in this case) is that you first check memcached, and if the key exists, you get the data from the cache. If it doesn't exist, you go to the database, get the data, stick it in memcached, then return it to the user.
For your use case here, you want a unique key for each unique record set you're returning. Take your example. If email address is the unique key you are using to identify a record set, then use that as the key in memcached. Pseudo-code:
<cfset results = variables.memcached.set(key="foo_#email#",value=test,expiry="5000")>
foo_ lets you provide a "namespace" for the keys you are putting into memcached, which makes it easier to manage and avoid key collisions.

Using coldfusion.sql.QueryTable.next() in Coldfusion 9

I am trying to write a custom tag that will iterate over a cfquery object in a special way. I found this page: http://www.zrinity.com/developers/mx/undocumentation/query.cfm outlining how to use the underlying java methods to navigate the result set, but it doesn't seem to be working in CF9.
I can call .next(), .previous(), .first(), and .last() just fine, and each method updates query.currentRow, but referencing query.columnName always returns the value from the first row, not currentRow.
Example:
<cfquery name="testQuery" datasource="source">
SELECT FooName FROM NumberedFoos
</cfquery>
<cfloop from="1" to="3" index="i">
<cfoutput>#testQuery.currentRow# => #testQuery.fooName#</cfoutput><br />
<cfset testQuery.next()>
</cfloop>
Produces:
1 => Foo 1
2 => Foo 1
3 => Foo 1
I know i could use something like testQuery.fooName[testQuery.currentRow], but that is pretty undesirable for the people I am making the custom tag for. Was the functionality described in the above link removed from CF9? If so is there an alternative?
EDIT
To expand on the why, the client wants a custom tag that allows them to "assert" certain things about a query. The client has a pretty low level understanding of CF, but are pretty solid writing SQL. Their desired end result is something akin to:
<cfquery name="purchaseTotals">
SELECT PurchaseId, Total FROM Purchases
</cfquery>
<CF_ASSERT query="purchaseTotals">
purchaseTotals.Total gte 0
</CF_ASSERT>
The desired output would be a html table with each row being the row from the query that fails the assertion. So to me, the CF_ASSERT tag need to be able to update the current row.
Edit 2:
The main challenge is to allow html in the body of the tag, while still having query values substituted from the appropriate row:
<CF_ASSERT query="purchaseTotals">
<CF_CONDITION expression="purchaseTotals.Total gte 0">
<!---error message when expression is false--->
<cfoutput>
Purchase #purchaseTotals.purchaseId# has a negative total!
</cfoutput>
</CF_CONDITION>
<CF_CONDITION expression="purchaseTotals.Total eq ''">
#PurchaseTotals.purchaseId# has a null total, this may be caused by:
<ul>
<li>Edge Case 1</li>
<li>Edge Case 2</li>
</ul>
</CF_CONDITION>
<CF_ASSERT>
The output here would be something like:
Purchase 120 has a negative total!
Purchase 157 has a negative total!
Purchase 157 has a null total, this may be caused by:
Edge Case 1Edge Case 2
Was the functionality described in the above link removed from CF9?
The internal stuff has definitely changed since the article was written in 2006. But I suspect the exact functionality you are describing may not have existed in any mx version. A key difference between your code and the linked examples is the usage of <cfoutput query=".."> (not just a plain <cfoutput>). The query attribute obviously provides some extra context when evaluating the variables. Remove it (like in your example) and the results are "the value from the first row, not currentRow.". Even under MX6, which does not bode well for subsequent versions. That exact functionality probably was not removed. It just never worked to begin with.
If so is there an alternative?
Like I said earlier, the cleanest approach would be to use array notion ie #query.column[row]#. Given that you seem to have rejected that option, you are basically left with evaluate(). You would need to loop through the query within the parent tag. Then use evaluate to process the subtag expression and content. It is not particularly elegant or simple IMO. But I think that may be good as it gets without array notation, or a ritual sacrifice of some kind.
ASSERT.cfm
<cfparam name="attributes.query" type="string">
<cfif thisTag.ExecutionMode is 'start'>
<!--- validate attributes.query is a query object --->
<cfif not ( structKeyExists(caller, attributes.query) AND IsQuery(caller[attributes.query]) )>
<cfthrow message="Attributes.query [#attributes.query#] is undefined or not a query object">
</cfif>
</cfif>
<cfif thisTag.ExecutionMode is 'end'>
<cfset variables[attributes.query] = caller[attributes.query]>
<cfloop query="variables.#attributes.query#">
<cfloop array="#thisTag.assocAttribs#" index="subTag">
<cfset variables.matchFound = evaluate(subTag.expression)>
<cfif variables.matchFound>
<cfoutput>[#currentRow#] #evaluate(DE(subTag.Content))#</cfoutput><hr>
</cfif>
</cfloop>
</cfloop>
</cfif>
CONDITION.cfm
Note: Do NOT use <cfoutput> tags within the tag content.
<cfparam name="attributes.expression" type="string">
<cfif thisTag.ExecutionMode is "start">
<cfassociate baseTag="CF_ASSERT">
</cfif>
<cfif thisTag.ExecutionMode is "end">
<cfset attributes.content = thisTag.GeneratedContent>
<cfset thisTag.GeneratedContent = "">
</cfif>
client has a pretty low level understanding of CF, but are pretty
solid writing SQL
Having said all that, are things being implemented this way because it is the best approach or because it is most similar to writing SQL ie comfortable ?
Classic example of the inner platform effect.
I would advise you not to do this as you are trying to create a system which mimics built in functionality of the base or running system which ultimately becomes a poorly implemented version of the system in which it is running on / implemented in.
Sounds confusing, I know - but this is a well known anti-pattern to avoid.
See here for more information : http://en.wikipedia.org/wiki/Inner-platform_effect
P.s. what you are looking for (although I disagree with the implementation) is itteration of a query outside of a cfloop, simply use array syntax :
#queryName.fieldName[rowNumber]#
Using this you can itterate the query however you wish, certainly no need for underlying java. Notice we aren't using queryName.currentRow. For previous() next() functionality, you just change the rowNumber up or down.

Session Variables, welcome messages

Why does this not work? My welcome message, it just doesn't show up:
<p>Welcome <cfoutput>#Recordset1.UserID#</cfoutput>.</p>
The session variable on the login page I created is:
<cflock timeout=999 scope="Session" type="Exclusive">
<cfset Session.IDUsers =''>
</cflock>
is this incorrect? On the index page where I'm trying to display my welcome message I have:
<cfquery name="Recordset1" datasource="cfGossip">
SELECT *
FROM users
WHERE users.IDUsers = <cfqueryparam value="#Session.IDUsers#">
</cfquery>
I'm not sure if this works, or is necessary?
If you set the userid stored in the session to be the empty string, when you query on it, you will only get users for whom the id is the empty string, which shouldn't be any of them. Therefore, the query is returning an empty set, and your page is (correctly) not displaying a user id.
How are you initially identifying the user? Are you querying against a database when they log in? Are you storing a cookie? Reading Tarot cards? For this to work, at some point, you have to store the correct userid, probably in the session. To do that, you need to first identify who the user is.
Also, if you are using CF6+, you probably do not need the cflock. It is now used to prevent race conditions, as CF is now thread-safe.
Looks like you're just starting with CF, welcome to the community.
My understanding of your code makes the structure look like the following, if I'm understanding you correctly:
<cfset session.idUsers = '' />
<cfquery datasource = "cfgossip" name = "recordset1">
SELECT * FROM USERS WHERE USERS.ID_USERS = <cfqueryparam cfsqltype = "cf_sql_integer" value = "#session.idUsers# />
</cfquery>
<cfoutput>Welcome #recordset1.userID#</cfoutput>
The reason this doesn't work is because your session.idUsers value is blank. Assuming you have a user in your database with an ID_USERS value of 1, you could change the CFSET to look like and it should return results.
Additionally, while it's great to see you using CFQUERYPARAM, I'd recommend including a CFSQLTYPE attribute in the tag whenever possible to provide an added line of defense against injection attacks. You can check out http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=Tags_p-q_18.html to see the list of available types.
Is there anywhere in your code where you set your session.IDUsers? You initialize it as a blank ''. Coldfusion does not populate it for you. The session scope is a place that will remember things for that user that you put there for a specified period of time inactivity, usually 20 minutes. So hopefully, somewhere before you run your query you have additional logic that fills that in, otherwise you are asking the database for a user named, ''.
This is just a point of style, but the following may work better for you:
<cfset Session.IDUsers =''>
<!--- Do something here to populate Session.IDUsers --->
<!--- Creates a blank query - not necessary, but can reduce errors later --->
<cfset Recordset1 = queryNew("UserID")>
<!--- Populate the query from the database --->
<cfquery name="Recordset1" datasource="cfGossip">
SELECT *
FROM users
WHERE users.IDUsers = <cfqueryparam value="#Session.IDUsers#">
</cfquery>
<!--- If the query has data, use it, otherwise show generic message --->
<cfoutput>
<cfif Recordset1.recordcount>
<p>Welcome #Recordset1.UserID#.</p>
<cfelse>
<p>Welcome new user!</p>
</cfif>
</cfoutput>
<!--- OR since we used queryNew("userID"), we can simplify without throwing an error. ---->
<cfoutput>
<p>Welcome <cfif len(Recordset1.userID)>#Recordset1.userID#.<cfelse>new user!</cfif></p>
</cfoutput>
Putting the cfoutput outside the paragraph block will make it easier if you have additional variables to insert into the text. (but will work either way)
Regardless of all that, unless you forgot to share a bit more of the code, I think the issue is that the session.IDUsers is blank and needs to be populated before the query. I hope this helps!