I'm pleased to announce the launch of my company's new website:
http://www.msl.com
It's been in the works for awhile, but since I'm the lone wolf doing all the content, design and development, it's taken awhile. Needless to say I'm happy it's done and quite pleased with the results. Of course, it's never really done! I had to hold back on many of the features I wanted to build, because we really just needed to move away from our old, static HTML tag-soup site. However like all sites, it's a work in progress and will continue to change over the coming months.
For the geeks, it's built with ColdFusion 7 and utilizing the Model-Glue framework. It also uses YUI for the menus and some of the layout, and SPRY for some of the other AJAX/DHTML stuff. The markup is XHTML/CSS, it did validate at one point but the YUI menus don't validate...bummer. Oh well! I've never been that sort of standardista...
Showing posts with label Model Glue. Show all posts
Showing posts with label Model Glue. Show all posts
Tuesday, April 10, 2007
Friday, April 6, 2007
Dreamweaver Extensions for Model-Glue XML (and FB and M-ii)
On a couple of occasions I have thought that someone needs to write a Dreamweaver extension for ModelGlue, as well as the other frameworks, especially a tag library (and a file browser/inspector wouldn't hurt - those ModelGlue.xml files can get long...). I can't tell you how many times I've habitually hit ENTER after typing "na..." to add a name attribute, which auto-completes in CF but not for XML.
Well at least there is a solution to my first desire! The ever-productive Massimo Foti has a series of Dreamweaver tag libraries for the popular frameworks. They can be found on www.massimocorner.com. The site seems to have a thing against deep linking, but you can find the .mxp file downloads under the Dreamweaver > ColdFusion section.
Thank you, Massimo!
(now I just need to find a file inspector, perhaps any good XML one would work...)
Well at least there is a solution to my first desire! The ever-productive Massimo Foti has a series of Dreamweaver tag libraries for the popular frameworks. They can be found on www.massimocorner.com. The site seems to have a thing against deep linking, but you can find the .mxp file downloads under the Dreamweaver > ColdFusion section.
Thank you, Massimo!
(now I just need to find a file inspector, perhaps any good XML one would work...)
Monday, April 2, 2007
Serving a Downloadable File with ColdFusion in Model Glue
I thought maybe it's time for another "real" blog post. I learned a little something today (with some help from Adam) so now I'll share. A hyperlink to download a PDF file - seems simple and straightforward enough. But I've been building this site using the Model Glue framework (and learning MG in the process -- so far so good!), and I wanted to serve the file with <cfcontent>, not just a plain hyperlink, so it wasn't as straightforward as just <a href="myfile.pdf">Download It</a>...
Here's my solution, I'd be interested to hear how others have done this. It seemed like a little bit overkill as I was writing it, but I also realized that I would never need to write this snippet of code again, so that kind of makes up for the long-windedness of it.
The solution is composed of all the standard files and snippets in a Model Glue application.
First, let's look at the "Download" Bean CFC: Download.cfc represents my "file" object that stores my file information. It basically just stores values for the file name, file path, and mime type of the file I'm serving for download. It lives in my "model" folder.
Notice that the CFC uses ExpandPath() so that a relative url can be passed in as the location of the file, which is much easier when it comes time to build your link, and then the app is not server-dependent. Yes, I could write setters and use those setters to construct the CFC, but I'm never going to build these objects to say, insert into a database, so I didn't bother.
Then I have the actual "download" include. It's called _download.cfm because the underscore preceding the filename is a common convention for files that should not be called directly, but need to be included in another file, it also differentiates it clearly from your "template" view files.
This file gets dynamically included on the page when the "download" viewState value is present. It also only serves the file if it's not a simple value (to ensure the values we need are there). It lives in the "views" folder. Notice there are separate values for the file path and the file name. The file name is what the user sees when they download the file, the file path tells CF where to locate the file. Being able to set the file name in the header is nice because you can use a short, sweet and simple file name for the end user, but your own convoluted, datestamped , purpose-coded file naming convention for the actual file (ok maybe that's just me).
Also we use the http header so that the user will be prompted with the browser "download" dialog, rather than leaving it to the browser to try and open the file directly. It prevents us from having to list some weird instructions like "right-click and choose Save Target As" for the less-savvy users (arent' they all).
My Model Glue Controller.cfc contains this function to build the Download object. It just calls the Download.cfc, creates the "download" object, and then adds the instance of said object to the event so it can be found when the page is rendered.
In My ModelGlue.xml file, I hook my application into the above function with this XML snippet.
The event handler for that message is...
The broadcast, obviously, requests the download object via the Controller.cfc. The result - "do" is the same page that my link is on - so the user does not leave that page when they click the "download" link, they are just served the file. Lastly, on the page where I want the link that is clicked to download this file, this is the <a> tag:
Nice. That's all there is to it.
Of course this sample doesn't show any security or anything, this would only be appropriate for a public download. Sorry if the spacing and indentation is ugly, Blogger's code formatting is a pain! Hope you find it helpful!
Here's my solution, I'd be interested to hear how others have done this. It seemed like a little bit overkill as I was writing it, but I also realized that I would never need to write this snippet of code again, so that kind of makes up for the long-windedness of it.
The solution is composed of all the standard files and snippets in a Model Glue application.
- the "model" CFC - in this case, a simple object
- a .cfm view file where the "cfcontent" resides
- a function in the Controller.cfc to create the download object and add it to the event
- a message broadcast, listener, and event handler in the ModelGlue.xml file
- all the views, which compose my page and contain the links to download the file
First, let's look at the "Download" Bean CFC: Download.cfc represents my "file" object that stores my file information. It basically just stores values for the file name, file path, and mime type of the file I'm serving for download. It lives in my "model" folder.
<!--- Download.cfc --->
<cfcomponent displayname="Download" hint="retrieves a file for download to be served with cfcontent">
<!--- init() --->
<cffunction name="init" access="public" returntype="any">
<cfargument name="fileName" type="string" required="yes" hint="name of file once downloaded">
<cfargument name="filePath" type="string" required="yes" hint="full path of file to be served">
<cfargument name="fileType" type="string" required="yes" hint="mime type of file">
<cfset filename =" ARGUMENTS.fileName">
<cfset filepath =" ExpandPath(ARGUMENTS.filePath)">
<cfset filetype =" ARGUMENTS.fileType">
<cfreturn this>
</cffunction>
<!--- getters --->
<cffunction name="getFilename" returntype="string" access="public" output="false">
<cfreturn filename>
</cffunction>
<cffunction name="getFilePath" returntype="string" access="public" output="false">
<cfreturn filepath>
</cffunction>
<cffunction name="getFileType" returntype="string" access="public" output="false">
<cfreturn fileType>
</cffunction>
</cfcomponent>
Notice that the CFC uses ExpandPath() so that a relative url can be passed in as the location of the file, which is much easier when it comes time to build your link, and then the app is not server-dependent. Yes, I could write setters and use those setters to construct the CFC, but I'm never going to build these objects to say, insert into a database, so I didn't bother.
Then I have the actual "download" include. It's called _download.cfm because the underscore preceding the filename is a common convention for files that should not be called directly, but need to be included in another file, it also differentiates it clearly from your "template" view files.
This file gets dynamically included on the page when the "download" viewState value is present. It also only serves the file if it's not a simple value (to ensure the values we need are there). It lives in the "views" folder. Notice there are separate values for the file path and the file name. The file name is what the user sees when they download the file, the file path tells CF where to locate the file. Being able to set the file name in the header is nice because you can use a short, sweet and simple file name for the end user, but your own convoluted, datestamped , purpose-coded file naming convention for the actual file (ok maybe that's just me).
Also we use the http header so that the user will be prompted with the browser "download" dialog, rather than leaving it to the browser to try and open the file directly. It prevents us from having to list some weird instructions like "right-click and choose Save Target As" for the less-savvy users (arent' they all).
<cfset download =" viewState.getValue('download')">
<cfif NOT isSimpleValue(download)>
<cfheader name="Content-Disposition" value="attachment; filename=#download.getFileName()#">
<cfcontent type="#download.getfiletype()#" file="#download.getfilePath()#" deletefile="no">
</cfif>
My Model Glue Controller.cfc contains this function to build the Download object. It just calls the Download.cfc, creates the "download" object, and then adds the instance of said object to the event so it can be found when the page is rendered.
<!--- Controller.cfc --->
<cffunction name="getMyDoc" access="public" returntype="void" output="false">
<cfargument name="event" type="any" required="yes">
<cfset fileDownload = createObject("component","model.Download").init('MyDoc.pdf','assets/docs/MyDoc_123.pdf','application/pdf')>
<cfset arguments.event.setValue("download", fileDownload)>
</cffunction>In My ModelGlue.xml file, I hook my application into the above function with this XML snippet.
<message-listener message="getMyDoc" function="getMyDoc" />
The event handler for that message is...
<event-handler name="download.mydoc">
<broadcasts>
<message name="getMyDoc" />
</broadcasts>
<results>
<result do="page.mydoc" />
</results>
</event-handler>
The broadcast, obviously, requests the download object via the Controller.cfc. The result - "do" is the same page that my link is on - so the user does not leave that page when they click the "download" link, they are just served the file. Lastly, on the page where I want the link that is clicked to download this file, this is the <a> tag:
<a href="#viewState.getValue('myself')#download.mydoc">Download My Doc</a>
Nice. That's all there is to it.
Of course this sample doesn't show any security or anything, this would only be appropriate for a public download. Sorry if the spacing and indentation is ugly, Blogger's code formatting is a pain! Hope you find it helpful!
Subscribe to:
Posts (Atom)