Turning to the Darkside...of <cfscript> 0 comments   |   Tags: ColdFusion   |   Sorta-Perma-Link
During a lull at work the other day, I was reading about the extended capabilities that have been added to <cfscript> in ColdFusion 9, like being able to create queries and entire components...

Honestly, until recently I've not really used a whole lot of .  I'm not really sure why...just not something that interested me a whole lot.  You think it would: I like JavaScript syntax a lot, and translating these skills to <cfscript> is pretty straightforward.  Still, not a whole lot of experience.

Honestly, I think a lot of it is that there's something comforting about the tag syntax.  It's easy to see a balance in your code, and even easier--depending on your IDE--to see where you've completely borked something, left out a tag...whatever.  Some of it, out of necessity, is that not every tag has a equivalent, although efforts are underway to remedy this...Whatever the reasons might be, it's super-simple to get used to the tags and then feel quite lost in <cfscript>.

The revelation for me was kind of interesting.  For starters, outside of the very specific tags like, say, <cfexecute>, what are the majority of the tags that you use in a regular swatch of code?  For me, it's a LOT of <cfset> and <cfif>.  What struck me when I started looking at these, however, is that even though I'm using a 'tag' to "wrap" the variables and values that I'm messing with, the rest of my operation is...what?  A ton of functions!  ListLen(), isDefined(), ArrayNew()...all of these have ABSOLUTELY NOTHING to do with tags whatsoever!

Yes, I know, it's painfully obvious.  However, this realization completely slapped me in the face.  If the vast majority of my code is not really dependent on these tags at all, why waste the keystrokes on all those greater-than and less-than signs?

Well, I'll be honest.  After about 5 minutes with <cfscript>, I was hooked. Outside of very specific needs (remember, tags CANNOT be used in a <cfscript> block...), this is what I'm using from now on. It's faster to write, takes up less space, and just feels a little more like I think a bit of code should. That's enough for me.  And given that each new version of CF continues to bring on more capabilities for this method, I feel like I'm on the right track.
Converting ColdFusion Queries for Ext 0 comments   |   Tags: ColdFusion   Ext   |   Sorta-Perma-Link

At my new job, we use Ext fairly regularly to get some pretty cool AJAXiness up in the mix of things.  To familiarize myself with Ext (I've not really used it until now), I recently downloaded the newest version and have been playing around with it quite a bit.

One of the elements I like quite a bit is the data grid.  Like most other things, Ext makes it pretty simple to pull off a slick sortable, pageable data grid with very little work.

Of course, given that I use ColdFusion alot, I decided to hook up a grid to a remote CFC call.  While the call itself is easy enough to do, there is one issue: the JSON that CF returns is in a format that Ext cannot quite understand.

Now there are a bunch of pre-built converters out there, some more robust than others (and a few I couldn't quite get to work).  

So in the spirit of trying to figure it out for myself, I wrote up a simple function in ColdFusion that will take a regular query and convert it into happy JSON that Ext can deal with.

First, here's our function to create our generic query:

<cffunction name="getPosts" access="remote" returnformat="json" output="false" hint="Gets list/detail of posts">
     <cfargument name="limit" type="numeric" required="no">
     <cfargument name="start" type="numeric" required="no">
     <cfargument name="sort" required="no" type="string">
     <cfset var rsPosts = "">
     <cfquery name="rsPosts" datasource="thegathering">
         SELECT postID,title,date,content
         FROM posts
         ORDER BY #arguments.sort#
     </cfquery>    
     <cfset clist = 'postID,title,date'>
     <cfset arrRecords = convertQueryToExtJSON(rsPosts,clist,arguments.limit,arguments.start)>
     <cfset s = {rows=arrRecords,dataset=#rsPosts.RecordCount#}>
     <cfreturn s />
</cffunction>

So the first 10 lines or so are pretty starightforward.  After getting the query, I populate a quick list of columns that I'd like to get turned into JSON goodness.  Next, I pass along some arguments to another function--convertQueryToExtJSON()--where the JSON transformation will happen.

Basically, to get the JSON transformation right, we need to create an array of structures in ColdFusion.  Once we have this, we can pass it back to our calling function and let the returnFormat=JSON basically do the rest.  The trick, of course, is making this array of structures.

As with most things, however, CF makes this super easy.  Here's my second function:

<cffunction name="ConvertQueryToExtJSON" returntype="any" access="public">
     <cfargument name="query" type="query" required="yes" />
     <cfargument name="clist" type="string" required="yes" />
     <cfargument name="limit" type="numeric" required="yes" />
     <cfargument name="start" type="numeric" required="yes" />
     <cfscript>
         arrRecords = arrayNew(1);
         if(arguments.start==0) {
             counter = 1;    
         }
         else {
             counter = arguments.start;
         }
         for(i=1;i<=arguments.limit;i++)  {
             strResults = structNew();
             for(x=1;x<=listLen(clist);x++) {
                 strResults[ucase(listGetAt(clist,x))] = query[listGetAt(clist,x)][counter];
             }
             arrRecords[i] = strResults;
             counter = counter+1;
         }
         return arrRecords;
     </cfscript>
</cffunction>

Again, pretty straightforward.  First, we create an empty, one-dimension array that will hold our structures.  Next, we start a loop that will progress for as many steps as are equal to the argument "limit" (e.g., our "page" size).  At each iteration, we create a new structure that will hold our query row results. Also, we will begin a loop over the column list that we passed in, setting a new key for every column that we want to return in our results.  Using bracket notation, we can easily match the column list item to the corresponding column in our query.

Finally, to close out each major iteration, we simply add our filled-struct to our master array; once all the looping is done, we can return the result.  That's it!

Now as I mentioned, I am certainly not the first person to do this, and I'm sure there are a lot grander solutions to this than what I've offered here (like using cfgrid!!!).  However, this feels pretty simple and elegant to me, so I'm happy with it for the time being.

EDIT: My apologies.  I was going to post a demo, but then realized that GoDaddy is dumb and only offers CFMX7...stupid GoDaddy...

Getting Web Site Screenshots with ColdFusion...and a Little Help 0 comments   |   Tags: ColdFusion   IECapt   Screen Shots   |   Sorta-Perma-Link

For those of you who don't know, I am the owner and occassional operator (!) of CSS {Imagine}, a simple, but relatively well-visited web design gallery.  One of the requirements I had for this site when I built it was that I did NOT want to have to do any work of manually grabbing screenshots and cropping thumbnails for the sites that are submitted to the site.  So using a combination of a third-party screen-capture service and CF8's super-awesome image editing tools, I've built an entirely automated process for handling of all this less-than-fun work.  Heck, all I have to do to kick off the process is to click a link in a notification e-mail...:)

One of the downsides to using the third-party service is that the results are VERY inconsistent.  Some of the images are garbled, while others simply fail.  However, the biggest issue I have is that the service has--reasonably enough--a finite amount of servers to process all the request.  Therefore, there are several occassions (daily) that I have to wait to kick off the process until the screen shot service has reached my requests in the queue.  I know, I know, not a huge deal.  However, it would be super awesome if I could do this on my own server.

What, exactly, would doing something like this entail?  Well, for starters, you're not going to get great results with a ColdFusion-exclusive option.  A while back, Ray Camden wrote a nice post about using to accomplish this.  In fairness, it works ok, but does not give consistent results.

So the next best option is to create an executable that you can run on your server from ColdFusion that will open a browser, grab a screen shot, and save it back to the file system.  

Fortunately, this has been done.  The other day, I ran across IECapt, a tiny command-line utility that you can use to grab images in a variety of formats and save to your server.  FYI, it's available in C++ and C#, and the source code is available if you feel the need to expand its functionality.

So with all this said, let's make a screen shot!  For this example, we want to end up with a 800x600 screen shot of http://coldfusionjedi.com.

First, we'll use . If you've not used before (this example was my first exposure to it...), it basically allows you to run a process on the server where ColdFusion resides.  Here's the code:

<cfset argArray = arraynew(1)>
<cfset argArray[1] = '--url=http://coldfusionjedi.com'>
<cfset argArray[2] = '--out=C:\ColdFusion8\wwwroot\project\images\cfjedi.png'>
<cfset argArray[3] = '--min-width=960'>
<cfset argArray[4] = '--delay=1000'>
           
<cfexecute    name="C:\ColdFusion8\wwwroot\theHub\IECapt.exe"
        arguments="#argArray#"
        timeout="100" />

Pretty simple. Here, I start by building an array of arguments to pass to the executable, including the site URL, where I want the file to be saved (including filename and image type), a minimum width for the screen capture, and how long the process should delay after the web page is loading before capturing the page.  Next, I simply pass these arguments to the executable on my server.

Now before we get into some image manipulation goodness, let me make a note about the "arguments". According to the documentation, you are *supposed* to be able to pass either a string or an array to the "arguments" attribute, and ColdFusion will pass them to Windows as needed.  However, in my tests, I found that I could not get the "string" version to work.  For example, I initially tried this:

<cfset argString = "--url=http://coldfusionjedi.com --out=C:\ColdFusion8\wwwroot\project\images\cfjedi.png" />

This worked fine.  However, when I added "--min-width=960", the process borked and I gave up.

On the other hand, passing all these arguments as an array works just fine.  I'm not sure what's going on, and more than likely I'm doing something wrong.  However, I thought I'd point it out in case anyone else runs into an issue.

Ok, enough of that. Now that our image is saved to the file system, we can do some CF image juju on it and get our desired result.  Here's what my code does:

<cfscript>
     image = imageNew(myFileName);
 
     // if the image is wider than we want, trim it down to the max width; leaving height blank
     // will also keep the aspect ration, which is generally good for image ;)
     try {
    if(image.width > 800) {
         imageResize(image,800,'');
    };
    // if the image is taller than we want, crop it down to the max height
    // since we already resized the image to the desired width, we can simply
    // crop the image down to the desired height and avoid squishing it ;)
    if(image.height > 600) {
         imageCrop(image,0,0,800,variables.600);
    };
    // all done kiddos! write this bad boy back to the file
    imageWrite(image);
</cfscript>

Nothing terribly complicated going on here.  First, we resize the entire image to the target width--800 px.  Once that's done, we can crop the image's height to our target, which is 600px.  Now that our fancy image is resized and cropped as needed, we can simply write the changes back to the file whose instance we created by specifying the absolute path in the imageNew() call at the very start of this function.

That's all there is to it.

Let me close with a few remarks.  I think IECapt is awesome, and I'm grateful to have found it.  However, as the author himself points out, it's on the level of "works for me." There are several features which I wish it had (such as the maximum height of the screenshot or using a different browser than IE)...so down the road I'll probably have to break down and develop something of my own.  

Anyway, hope this is helpful to someone!