Archives for the year of: 2007

ColdFusion makes developing web applications insanely quick and easy. I don’t need to worry about the nuts and bolts of how to connect to a database, or how to invoke a web service, or how to read and write files on the server. I just write straightforward tags in my favorite text editor — and I’m done, web application launched for all the world to use.

But there are some things that web applications can’t do. They can’t really read and write files on the client machine. They can’t interact much with the client operating system. Drag and drop, PDF manipulation, copy and paste… the list goes on. And all that aside, an Internet application will never work without an Internet connection.

So how do you go about creating something that can do those things? A couple years ago, you were stuck building complex and clunky binary applications. So you have to dig into lower level languages like Java, C++, or any of the .Net languages.

Suddenly you’re working with compilers and writing 300 commands to do what one tag does in ColdFusion. Sure, you’re scoring points with the Java and .Net purists. But all that doesn’t leave a ton of time for the trivial things like the interface or user experience.

Enter the Adobe Integrated Runtime — AIR. You can use AIR to write a desktop application in Flex XML and ActionScript, or HTML and JavaScript. AIR exposes hooks in to the client system to handle a local database connection, catch drag and drop events, open up local files, and even render PDF documents.

So instead of writing complex machine code, you’re back to writing tags to define the interface. And you’re calling a simplified scripting API to do heavy lifting under the covers. And the API runs on both Windows and Mac, so your pool of potential users is not limited to one operating system.

Have you already written a full web application in Flex or AJAX? You’re only a few steps away from adapting the same code to run either online, or on the desktop. Maybe it’s a little of both.

Flickr has offered a pretty slick upload tool for a few years now. It’s an executable that you can install on your desktop to do drag and drop file uploads. Somebody at Flickr had to develop separate binary Windows and Mac executables.

With AIR, that’s something you could in one shot. Don’t believe me? Check out Matt Chotin’s MediaWiki uploader AIR app. They’re different underlying services, but it’s a great example of how a web app can be extended to the desktop.

AIR does a lot to bridge the gap between web and desktop apps. But just like ColdFusion did for web scripting, it makes building all the parts easy. And you get to use tools that you already know exactly how to use.

Now you can get back to focusing on what’s really important: building better software.

I just posted the code for a dual select box ColdFusion custom tag called cf_SelectSwap.

It takes a query of items and produces two DHTML list boxes. Users can pick items from the available list and move them to the selected list. The selections are stored to two form variables. The first is a list of selected values, the other a list of selected display text values.

If a user has JavaScript disabled, it degrades down to a single list box with items selected. It can be used multiple times within the same cfm template.

Nothing earth shattering, but I put it together in the middle of a project I am working on. Maybe someone else can get some mileage out of it.

I’ve been using Ray Camden’s and Adam Podolnick’s ColdFire Firebug extension for a little while now. For those who haven’t tried it yet, the Firefox extension displays ColdFusion debugging info in a separate panel instead of appending it directly to the bottom of each page. It’s powerful stuff if you’ve ever had a page layout mangled by the vanilla debug dump.

Part of the setup for using ColdFire is deploying a customized debugging cfm template to your server. I never knew this — but ColdFusion actually uses a cfm template to parse and format the debugging output that ends up on each page. You can actually edit this file to customize the output on your system.

I wanted to get ColdFire working on our shared development server at work. But I knew not all of the developers would be on board to start. Luckily, the debugging cfm executes within each application. So the session and application variable scopes are available inside it.

The server debug template can alternate between the default debugging dump and ColdFire formatted output by checking for the existence of a session variable. By default, the server will append the default debug information. But each developer can set a session variable inside an application event or on individual templates to fire the ColdFire formatted output.

This is what your debug template looks like:

<cfif IsDefined("SESSION")
   AND IsStruct(SESSION)
   AND StructCount(SESSION) GT 0
   AND IsDefined("SESSION.UseColdFireDebugging")
   AND SESSION.UseColdFireDebugging EQ true>

   <!— Place the contents of ColdFire.cfm here —>

<cfelse>

   <!— Place the contents of your classic.cfm or custom debug template here —>

</cfif>

There’s lots of ways right now to extend the web outside of a traditional web browser. Whether you call them gadgets or widgets, web-enabled desktop mini-apps can be a powerful way to bridge the divide between your users’ machines and your Internet applications.

With all the gadget platforms out there, it can be tough to sift through the options. But in reality, most of the gadget engines are nothing more than glorified web page renderers. If you know HTML and you’re comfortable with JavaScript, you’re already well on your way to churning out your own custom gadgets.

(more…)

Twitter has been all the rage amongst web geeks for a few months now. But as the service starts to catch on with the general public, keeping up on all the Tweet messages can get a little crazy. It only gets crazier as you start following tweets from your coworkers, ColdFusion gurus, the Mac community, and, oh yeah, Jack Bauer. With a little help from Yahoo Pipes, you can organize and separate tweets into different RSS feeds.

Twitter exposes most of its service functionality through an extensive API system. The API is powerful and insanely simple. On top of that, most of the tweet lists are available in both RSS and JSON form. Using just a few lines of scripting code, you can bring all the elements of Twitter to your own web site or desktop gadget/widget.

Take this a step farther, and you can pull the RSS feeds for individual tweet streams into a single Yahoo Pipe. Pull the pipe through a sort operator on the publication date and the various Twitter streams intermingle into one timeline. The pipe has its own RSS feed that you can follow in your feed reader of choice or pull into your own applications.

I just threw together two quick examples, a ColdFusion Tweet pipe and a Flex Tweet pipe. Neither is exhaustive but there’s enough there to get the basic idea.

This is some very cool stuff, a perfect example of opening up your applications so your users can decide how to consume them.

A coworker of mine has been researching secure Flex 2 remote object calls to ColdFusion cfc’s over HTTPS. Seems simple enough, but anytime sensitive information is passed over the Internets I’d really like it to be bulletproof. Sure, I’m comfortable with HTTPS, but opening a flash swf over HTTP and trusting that it’s using HTTPS behind the scenes makes me a queasy system admin.

By default, a Flash movie can only access data through the exact domain where it was itself accessed. So, when you open a Flash swf over HTTP it can’t open a remote object over HTTPS to the same domain. The data host server can be tweaked to allow HTTP to HTTPS communication. But do I really want to make this tweak?

Adobe has this to say about it in a Flash tech note:

A secure server that allows access to movies hosted via a non-secure protocol
It is not advisable to permit HTTP content to access HTTPS content. This practice can compromise the security offered by HTTPS.

Most of the developers using our CF servers for Flex purposes are only using HTTPS for authentication. The rest of their app data is generally non-sensitive and doesn’t require encryption.

Is there really a security risk in opening a swf over HTTP and then making HTTPS remote object calls? If the endpoint is set in the Flex 2 app to an https address, all signs indicate that communication from the swf is going over HTTPS.

Microsoft offers up its view on what makes for a good gadget in a document buried under their Windows Vista User Experience Guidelines: Windows Sidebar Gadgets.

Despite being hidden away on the MSDN site, this is actually a really focused, useful set of guidelines for anyone thinking about creating desktop gadgets.

After some basic info on what a gadget is, the document explores what type of functionality belongs in a desktop gadget. There are visuals comparing examples of good and bad gadget layouts. There’s also guidelines for making the most of the Sidebar gadget framework, advice on handling different states of interaction visually, and recommended sizing standards.

I’ve been keeping this under wraps, but since I turned in the signed contracts there’s really no turning back. I’m working on a book about Windows Vista Sidebar gadgets.

The book’s part of a series called Visual Blueprint targeted to technically inclined people looking to learn a new skill. It’s really for people who have put together a few web sites and want to learn how to make interactive gadgets for the Vista desktop. It covers all the basics you need to make a gadget with lots of helpful screen-shots.

It’s going to take up most of my time for the next few months, but I’m really happy to be working on it. I think it’s a great subject — gadgets of all types are really going to change the way people interact with their desktop and with the web. It’s exciting to help people create their own gadgets.

One of my first projects in ColdFusion was a tool that compares files between two different folders and reports differences. The tool was intended for developers to identify differences between code in different phases of development. It wasn’t very fast, and when run against a large amount of files it crapped out completely.

I recently overhauled the code and was able to make it much faster. Maybe someone else will find it useful.

In the process I learned a few things:

  • <cfdirectory action=”list” recurse=”yes” /> is not always efficient. Directly using java.io.file can be dramatically faster — up to 200% faster when listing files & directories from a network location.
  • Opening two files into memory using <cffile> and comparing the contents is not the most efficient way to tell if they are different. Duh.
  • Recursively calling a function is a great way to walk through multi-level data.

I ended up tightening things down to two functions. BuildFileDictionary walks through a given base folder using java.io.File path and builds a struct of file information. The struct is keyed on the relative path of each file, thus the FileDictionary naming. This way, I can access information for a specific file using simple code like this: FileDictionary[relativeFilePath]. It also helps determine which files from one source do not exist under another by simply checking for the existence of a struct key.

The second function, CompareFileDictionaries compares two file dictionary structs and returns a couple lists: files only in dictionary one, files only in dictionary two, and files in common but out-of-synch. I settled on file size and last modified date as an acceptable basis to indicate file differences. There are other indicators, but these values were the most efficient to access.

BuildFileDictionary

<cffunction name="BuildFileDictionary" returntype="struct" access="public" output="false"
hint="Recurses through a directory, building a struct with file information. The result struct is keyed on each file's path relative to the passed-in base path. ie - details on TopFolder/SubFolder/File.txt are accessed through ReturnStruct['TopFolder/SubFolder/File.txt']">

<cfargument name="BasePath" type="string"
hint="Full path to the folder you want to build a dictionary for. UNC paths are acceptable.">
<cfargument name="ExcludeFileExtensions" type="string"
hint="List of file extensions to exclude from results.">
<cfargument name="ExcludeDirectories" type="string"
hint="List of directories to exclude from results. Leave off trailing / for each.">
<cfargument name="Files" type="any" default="-999" required="false"
hint="Optional Java File collection to recurse. Leave this arg off to start with the BasePath location.">
<cfargument name="FileDictionary" type="struct" default="#StructNew()#" required="false"
hint="FileDictionary to append results to. Leave this arg off to start fresh.">

<cfset var fileWalker = ArrayNew(1) />
<cfset var fileCounter = "" />
<cfset var relFilePath = "" />

<!--- Grab collection of files from Java.io.File if not passed in --->
<!--- Java.io.File is much faster than cfdirectory action='list' especially when accessing UNC paths --->
<cfif arguments.Files EQ -999>
<cfif DirectoryExists(BasePath)>
<cfset arguments.Files = createObject("java","java.io.File").init(arguments.BasePath).listFiles() />
<cfelse>
<cfthrow type="Application" message="BasePath does not exist" detail="The provided BasePath does not exist. Please enter a valid BasePath." />
</cfif>
</cfif>

<cfset fileWalker = arguments.Files />

<!--- Loop through current level of files and directories --->
<cfloop from="1" to="#ArrayLen(fileWalker)#" index="fileCounter">
<cfset relFilePath = Replace(fileWalker[fileCounter].getAbsolutePath(),arguments.BasePath,"") />

<!--- Recursively call this function for sub-directory --->
<cfif fileWalker[fileCounter].isDirectory()>
<cfif ListFindNoCase(arguments.ExcludeDirectories,relFilePath) EQ 0>
<cfinvoke method="BuildFileDictionary">
<cfinvokeargument name="BasePath" value="#arguments.BasePath#">
<cfinvokeargument name="ExcludeFileExtensions" value="#arguments.ExcludeFileExtensions#">
<cfinvokeargument name="ExcludeDirectories" value="#arguments.ExcludeDirectories#">
<cfinvokeargument name="Files" value="#fileWalker[fileCounter].listFiles()#">
<cfinvokeargument name="FileDictionary" value="#arguments.FileDictionary#">
</cfinvoke>
</cfif>
<!--- Grab details about this file --->
<cfelseif fileWalker[fileCounter].isFile()>
<cfif ListContainsNoCase(arguments.ExcludeFileExtensions,ListLast(relFilePath,".")) EQ 0>
<cfset arguments.FileDictionary[relFilePath] = StructNew() />
<cfset arguments.FileDictionary[relFilePath].FileName = fileWalker[fileCounter].getName() />
<cfset arguments.FileDictionary[relFilePath].FilePath = fileWalker[fileCounter].getPath() />
<cfset arguments.FileDictionary[relFilePath].AbsolutePath = fileWalker[fileCounter].getAbsolutePath() />
<!--- The hash value can be used to identify file contents, but it seems to slow things down to grab it --->
<!--- <cfset arguments.FileDictionary[dictionaryKey].HashCode = fileWalker[fileCounter].hashCode() /> --->
<cfset arguments.FileDictionary[relFilePath].LastModified = fileWalker[fileCounter].lastModified() />
<cfset arguments.FileDictionary[relFilePath].Size = fileWalker[fileCounter].length() />
</cfif>
</cfif>

</cfloop>

<cfreturn arguments.FileDictionary />

</cffunction>

CompareFileDictionaries

<cffunction name="CompareFileDictionaries" access="public" output="false" returntype="struct"
hint="Compares two file dictionaries and returns a struct containing files only in one, files only in two, and common files out of synch.">

<cfargument name="fileDictionaryOne" type="struct" required="yes" hint="File dictionary one">
<cfargument name="fileDictionaryTwo" type="struct" required="yes" hint="File dictionary two">

<!--- Variable declarations --->
<cfset var comparisonResults = StructNew() />
<cfset var relFilePath = "" />

<!--- Build up struct properties for results --->
<cfset comparisonResults.NamesOneOnly = ArrayNew(1) />
<cfset comparisonResults.NamesTwoOnly = ArrayNew(1) />
<cfset comparisonResults.OutOfSynch = false />
<cfset comparisonResults.NamesCommonOutOfSynch = ArrayNew(1) />

<!--- Loop through the relative file paths in the first file dictionary checking if the --->
<!--- same file relative path exists in the second file dictionary. If it does exist in --->
<!--- second dictionary, check if files attributes are out of synch between the two. --->
<cfloop list="#ListSort(StructKeyList(arguments.fileDictionaryOne),"textnocase","ASC")#" index="fileRelativePath">
<cfif StructKeyExists(arguments.fileDictionaryTwo,fileRelativePath)>
<cfif (arguments.fileDictionaryOne[fileRelativePath].LastModified NEQ arguments.fileDictionaryTwo[fileRelativePath].LastModified)
AND (arguments.fileDictionaryOne[fileRelativePath].Size NEQ arguments.fileDictionaryTwo[fileRelativePath].Size)>
<!--- File last modified data and file size does not match, add to NamesCommonOutOfSynch array --->
<cfset ArrayAppend(comparisonResults.NamesCommonOutOfSynch,fileRelativePath) />
</cfif>
<cfelse>
<!--- File does not exist in second dictionary, add to NamesOneOnly --->
<cfset ArrayAppend(comparisonResults.NamesOneOnly,fileRelativePath) />
</cfif>
</cfloop>

<!--- Loop through the relative file paths in the second dictionary checking if each exists --->
<!--- in the first dictionary. --->
<cfloop list="#ListSort(StructKeyList(arguments.fileDictionaryTwo),"textnocase","ASC")#" index="fileRelativePath">
<cfif not StructKeyExists(arguments.fileDictionaryOne,fileRelativePath)>
<cfset ArrayAppend(comparisonResults.NamesTwoOnly,fileRelativePath) />
</cfif>
</cfloop>

<cfif ArrayLen(comparisonResults.NamesCommonOutOfSynch) GT 0
OR ArrayLen(comparisonResults.NamesOneOnly) GT 0
OR ArrayLen(comparisonResults.NamesTwoOnly) GT 0>
<cfset comparisonResults.OutOfSynch = true />
</cfif>

<cfreturn comparisonResults />

</cffunction>

I needed to set the expiration date for Active Directory accounts in a VB.net web service today. Sounds simple, but it’s actually a bit tricky.

Turns out there’s a way to get around using the nonsensical large integer that AD uses to store the accountExpires field:

Dim NewADObject As DirectoryEntry
Dim ADContainer As DirectoryEntry

ADContainer = New DirectoryEntry(adPath,user,pass,authtype) NewADObject = ADContainer.Children.Add(cn,objectClass)

NewADObject.Properties("SAMAccountName") = "newusername"

... yadda yadda yadda ...

'Set expiration date for account Dim expireDate As DateTime = DomainAccountExpDate NewADObject.NativeObject.AccountExpirationDate = expireDate

NewADObject.CommitChanges()

The bold bit is the pertinent section. NativeObject allows direct access to the properties of the underlying DirectoryEntry COM component. AccountExpirationDate can be set as a DateTime value.