Since launching my little Room Booking system as an open source project, I thought it might be useful to highlight a few techniques used which others might find useful. Obviously, this isn’t to say that this is the ‘only way to do x’, but merely to flag some ways to approach common problems. All the below can be seen in context at the gitHub repo
events/functions.cfm
One issue a had a while back was determining the IP address of a user. Now, historically, this wasn’t an issue, as cgi.remote_host worked fine. However, since Railo’s installer changed the way it forwards requests from Apache (mod_jk-> mod_proxy), it always saw the remote IP as 127.0.0.1. This function checks the incoming request header and tries to grab the IP there, rather than in the CGI scope.
<cffunction name="getIPAddress" hint="Gets IP address even from Railo" returntype="String">
<cfscript>
var result="127.0.0.1";
var myHeaders = GetHttpRequestData();
if(structKeyExists(myHeaders, "headers") AND structKeyExists(myHeaders.headers, "x-forwarded-for")){
result=myHeaders.headers["x-forwarded-for"];
}
return result;
</cfscript>
</cffunction>
events/onrequeststart.cfm
This simply checks every incoming request for the URL or FORM scope, loops over each key in the struct, and trims the whitespace.
<cfif StructCount(form)>
<cfloop collection="#form#" item="key">
<cfset form[key] = Trim(form[key])>
</cfloop>
</cfif>
<cfif StructCount(url)>
<cfloop collection="#url#" item="key">
<cfset url[key] = Trim(url[key])>
</cfloop>
</cfif>
Personally, I think cfWheels should do this by default, but then, it would probably break a load of people’s applications. Simply put, I can’t imagine a case where I’d want whitespace at the beginning or end of any key in my url or form scope.
view/helpers.cfm
This one was an interesting one – Javascript often passes it’s values in Epoch time, i.e number of seconds since 1970; I needed to convert this to localTime in a few places. Surprisingly, I’ve never had to do this before!
<cffunction name="eToLocal" hint="convert epoc to localtime">
<cfargument name="e">
<cfreturn DateAdd("s", arguments.e ,DateConvert("utc2Local", "January 1 1970 00:00"))>
</cffunction>
So Bootstrap3 has panels. I love panels. But I’m lazy, I don’t want to type out the markup for a panel all the time. So these two helpers do it for me:
<cffunction name="panel" hint="Renders a bootstrap Panel">
<cfargument name="title" required="true">
<cfargument name="class" default="panel-default">
<cfargument name="ignorebody" default="false">
<cfset var r ="">
<cfsavecontent variable="r"><Cfoutput>
<!--- Start Panel --->
<div class="panel #arguments.class#">
<div class="panel-heading">
<h3 class="panel-title">#arguments.title#</h3>
</div>
<cfif !arguments.ignorebody>
<div class="panel-body">
</cfif>
</Cfoutput>
</cfsavecontent>
<cfreturn r />
</cffunction>
<cffunction name="panelend" hint="Close Panel">
<cfargument name="ignorebody" default="false">
<cfif !arguments.ignorebody>
<cfreturn "</div></div>" />
<cfelse>
<cfreturn "</div>" />
</cfif>
</cffunction>
Usage:
#panel(title="foo")#
.. things here
#panelEnd()#
Sometimes Bootstrap needs it’s content to be flush with the panel, so you occasionally need to ignore the panel-body – flush list-group-items – I’m looking at YOU.
#panel(title="foo", ignorebody=true)#
<ul class="list-group">
<li class="list-group-item">FOO</li>
</ul>
#panelEnd(ignorebody=true)#
Previously, I’d have done this using custom tags, i.e , but with the added import needed on every page, I find this a *lot* quicker/easier.
views/layout.cfm
One of things I like to do is have all the CSS in the header, and all the JS in the footer. So what happens when you want to do your JS stuff in your view folders? Here’s one solution..
In your view file, simply wrap your JS bits in cfsavecontent, i.e
<cfsavecontent variable="request.js.mystuff">
<script>// JS goes here</script>
</cfsavecontent>
Then in your layout file, after all your calls to jQuery etc:
<cfif structkeyexists(request, "js")>
<cfloop list="#structKeyList(request.js)#" index="key"><cfoutput>#request.js[key]#</cfoutput></cfloop>
</cfif>
The only thing this doesn’t really deal with is calling JS in a specific order, as structs are by their nature, unordered. I guess you could turn this principle into an array based system easily enough though: generally speaking, I haven’t found needing to write JS over many view files which needs to be executed in a specific order. YMMV.
bookings/index.cfm
This bit of JS is very useful:
$('body').on('hidden.bs.modal', '.modal', function () {
$(this).removeData('bs.modal');
});
Bootstrap caches modals which have had dynamic data inserted in, so initially, when I was building it, every time I clicked on an event, the modal would display the first event I’d click on’s data. This destroys the modal data after you close it. Handy.
And another thing…
The last thing to note is that this has an example of using Ajax in cfWheels, examples of which are fairly rare. If you’re interested in how that’s done, look at the javascript call in bookings/index.cfm under event sources: it’s just a jQuery ajax request. Now look at controllers/Eventdata.cfc = you’ll see the renderWith call, and if you look at the function ‘prepdata’, you’ll see me converting a query into a array of data (in the format I wanted it), which is then passed back by wheels using renderWith(events);
So there you go. If, of course, you guys have better ways of handling the above, I’d love to hear them.