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:
<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>
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!