Today's Best of ColdFusion 9 is Document Repository by Nathan Strutz. As you can guess by the title, the application handles document uploads. It has a simple security system with user registration. Once logged in, you can upload a document to the repository. Nathan's code will create a thumbnail, store a record in the database, and add the document to your list. Functionality wise it's only partially there right now, but there are some pretty darn interesting things in the code and it's a great start. Let's take a look.
First and foremost, I want to give Nathan huge brownie points for creating an installer.

And actually - not only did he create an installer, he created an uninstaller as well. His application handles setting up the DSN as well as creating a collection. I know that within all my open source applications, the lack of an installer is a real glaring omission. More applications should do this. Oh, and his installer is pretty smart too. On a whim I tested running the installer again, and it correctly saw that things were setup already. If you look at one thing from this application, make sure it's the installer code. Surprisingly he put this within a CFC in his model. Normally I'd have this as a separate script. (By "normally" I mean if I ever got off my lazy butt and did an installer.) This seemed odd to me at first glance but I can see some logic behind this. It is application specific even if run only once.
After setting the application up, you are presented with the main UI. The design of this application is very well done. Maybe it's that I'm getting older, but I love designs that make use of large text. It just makes things easier to read, more immediate, and overall just more bold. Again though - I'm slowly becoming an old fuddy duddy so it may just be my eyes. Registration (and login) is done with the cfwindow tag. After you register you can immediately begin uploading files:

Nathan recommends testing with images and said Word documents work as well. I was able to get PDF working, but a SWF file threw an error. I uploaded a few documents and noticed a bug with the thumbnail. It seemed to only use the thumbnail graphic for the first file uploaded.

But outside of that bug, it worked as expected. There is no way currently to edit documents, but you can download them and see a bit of meta information about the document. These features could be added quickly enough.
The admin is - unfortunately - all simply mocked out - but again - I love the "fat" UI here. It is incredibly simple and direct.

So let's talk a bit about the code. I'm happy to see this is another example of Framework One, the new framework by Sean Corfield I blogged about earlier. I actually plan on blogging on it more once I get past the contest.
In terms of components, his code is 100% script based. For folks curious what a 'full on' script based model CF9 application looks like, this is a good example. I can say that after using scripts for my CFCs in my Picard project, it definitely speeds up development. A cool aspect to his code is that he wrote a nice script wrapper for tags not yet supported in cfscript. The only non-script based CFC is SimulatedCFTags.cfc. I've pasted the complete code below:
<cfcomponent output="false">
<cffunction name="simulateCFCollection"><cfcollection attributecollection="#arguments#" name="local.value"/><cfif structKeyExists(local,"value")><cfreturn local.value/></cfif></cffunction>
<cffunction name="simulateCFIndex"><cfindex attributecollection="#arguments#"/></cffunction>
<cffunction name="simulateCFSearch"><cfsearch attributecollection="#arguments#" name="local.value"/><cfreturn local.value/></cffunction>
<cffunction name="simulateCFFile"><cffile attributecollection="#arguments#" result="local.value" /><cfreturn local.value/></cffunction>
<cffunction name="simulateCFDocument"><cfdocument attributecollection="#arguments#"/><cfif structKeyExists(local,"value")><cfreturn local.value/></cfif></cffunction>
<cffunction name="simulateCFPDF"><cfpdf attributecollection="#arguments#"/><cfif structKeyExists(local,"value")><cfreturn local.value/></cfif></cffunction>
<cffunction name="simulateCFSpreadsheet"><cfspreadsheet attributecollection="#arguments#"/><cfif structKeyExists(local,"value")><cfreturn local.value/></cfif></cffunction>
</cfcomponent>
As you can guess, this lets him use features that aren't supported yet in script. I was going to object to his use of "simualte*" for the names, but it's actually a really good idea. If ColdFusion 10 adds support for these functions he simulated, he won't have to worry about any kind of name collision.
Here is one sample call just to give you an idea:
variables.beanFactory.getBean("CFTags").simulateCFCollection(action="delete", engine="solr", collection=variables.beanFactory.getBean("config").get("collectionName"));
Pretty nifty I think.
Random note - just found this comment:
will throw a gray error if there's a problem
Not sure what a "gray error" is but I thought that was kind of funny.
The next thing I'd like to point out is his use of dynamic file processors. If you look at UploadedItem.cfc, you can see this bit of code:
return beanFactory.getBean("itemProcessor_" & getItemType());
This returns a dynamic CFC based on the file type of what you upload. If you look at the itemprocessors folder you can see what is supported and how to add support for additional file types.
So a few small nits. I had issue with the cfclocation argument in his Application.cfc. His code uses:
cfclocation = getDirectoryFromPath( getCurrentTemplatePath() ) & "com/dopefly/documentrepository/"
Which didn't work for me. It appears as if it needs to be relative only. I modified my code to:
cfclocation = "com/dopefly/documentrepository/"
Secondly - and this is a real small thing. When downloading documents, the filename ended up being index.cfm.jpeg. Don't forget that you can set a filename for stuff you push to the user via cfcontent. Actually - I just double checked the code. He does supply a filename like so:
<cfheader name="content-disposition" value='attachment; filename="#local.rc.file.getOriginalFileName()#' />
<cfcontent file="#local.rc.repo & local.rc.file.getFileName()#" type="application/unknown">
So maybe it simply isn't working in Chrome. I can say that the code I've used in the past uses inline, not attachment:
<cfheader name="Content-Disposition" value="inline; filename=cookbook.pdf">
<cfcontent type="application/pdf" reset="true" variable="#result#">
Anyway, you get the idea. So - in summary. Very nice beginning to a document repository. It's got an excellent UI already and if it just gets feature complete, it could be a great open source application! Download, play, and comment!
Archived Comments
Maybe it's just a copy-and-paste error, but shouldn't the <cfheader> tag be called as:
<cfheader name="content-disposition" value="attachment; filename=""#local.rc.file.getOriginalFileName()#""" />
Notice the use of extra quotes around the filename string.
Actually, he has a quote in front - see it? But not in back. I bet that could be it.
Yeah, I see it. I don't see a trailing one though, hence my comment about a possible error in copy-and-paste.
Personally, I prefer to not use single-quotes (') for attribute values (throws off syntax highlighting in Homesite+ [yeah, I'm 'old-school']) and will just double-up any double-quotes within attribute values. For some reason my eye is more easily able to parse the doubled-up double-quotes.
It helps if I read your _entire_ comment. ;) But yeah, I think thats the issue.
Thanks for being so kind to my sad little app, Ray!
Oh, and "gray error" - it's the standard CF error with the gray background. It was with the installer, if you give an incorrect cf admin password it would just throw an error that I didn't feel like catching (another item on the infinite to-do list).
I totally agree with all Ray said. An excellent peace of code.
+ what I liked:
a) devnotes.txt
I am sooo lazy to do that and I regretted so many times I did not. I envy you on this :)
b) Use of Ant. Not so common to see that employed in non-Java environment.
Questions:
- Why you ignored "service by convention" and did not use auto-wiring by any mean?
- Partially related to first Q. What motivated you to write your own bean factory?
As this is second entry with setup wizard, and for Nth time saw how useful that is, I am definitely going to create one for my app(s).
IMHO, this app is very good demo of how CF applications is going to look like (organized and written) in near future (for an year or two).
@Marko: Actually, I see many intermediate/advanced CFers use Ant. I don't use it often, but have some experience with it.
Nathan, this looks really cool and I love the UI (though not the background color).
I'm getting a really weird error during the setup:
Error getting collection information.: org.apache.solr.common.SolrException: Not Found Not Found request: http://localhost:8983/solr/admin/cores?action=STATUS&core=&wt=javabin
The error occurred in /Applications/ColdFusion9/wwwroot/best9/CF9DocRepo_Build/com/dopefly/SimulatedCFTags.cfc: line 3
Called from /Applications/ColdFusion9/wwwroot/best9/CF9DocRepo_Build/com/dopefly/documentrepository/DocumentRepositorySetup.cfc: line 13
Called from /Applications/ColdFusion9/wwwroot/best9/CF9DocRepo_Build/setup/setup.cfm: line 21
I'm on a Mac and have manually started solr. Any thoughts? I think I might have messed something up during install...
@Marko, Thanks for your comments. In response...
1) I ignored FW/1's services and autowiring because there was no documentation when I started writing it, and it wasn't immediately obvious how to make it work the way I wanted, so I dropped it.
2) I didn't want to incorporate any more files, external libraries, etc, further complicating stuff. The "manualwiring" was the easy solution. I'm for them, generally, and would probably shoot for Lightwire as a bean factory framework.
@Marko, @cfjedimaster, a story: I was trying to tweak things and get it in a releaseable state, rushing through bug after bug just to make it load right, squeezing every second, cramming. It was 11:57PM on Nov. 30th. Two clicks later, I had a zip file the the app in it. Less than a minute later and the email with my entry was sent - Ant TOTALLY saved my skin! The time I put into it early on completely paid off.
@Sam_Farmer I think that's the error you get when CF can't talk to Solr. Honestly, the app will work the same without Solr, just the setup app will bomb. The background color? I thought dark grays were the new lime green, or something...
@Sam_Farmer I posted this on your blog but want to mention it here in case anybody else runs into this. I was getting the same error this morning. I had created an alias to cfusion/solr/cfsolr to allow me to start solr from any location in terminal. The problem is, cfsolr looks for start.jar without specifying a path to it, and if you're not running the command from the same directory (cfusion/solr/) it's not going to find it. To further complicate things, it suppresses the error message and tells you ColdFusion Solr Server is starting up and will be available shortly, leading you to believe everything is peachy.