Forum OpenACS Q&A: Re: Squirting out CSV files

Collapse
Posted by Jason B. Standing on

Thanks for the tipoff Raul.

I tried the code snippet mentioned in https://openacs.org/forums/message-view?message%5fid=146238#149559 however the fundamental problem still stands that template::list::write_csv uses ns_return to return its output rather than returning a variable to the calling function. After looking at the code in list-procs.tcl it appears that template::list::write_output - if the format type is set to csv - simply calls template::list::write_csv and therefore I'm no better off than I was before.

Am I missing something fundamental here with ns_return ? My understanding/interpretation was that ns_return writes data straight out to the connection/template and as such can't be captured in a variable. Is this incorrect ?

By using the code in Jade & Randy's discussion I can get the browser to spit out an Excel-recognised CSV file, however as

set csv [template::list::write_output -name list_test]

doesn't actually prime the variable with data, the resulting CSV file is 0 bytes !

The solution I've put in place is to add a function to list-procs.tcl which is a direct copy of template::list::write_csv however I've substituted the return line

ns_return 200 text/plain $__output

with

set headers [ns_set new myheaders]
ns_set put $headers "Content-disposition" "attachment; filename=$file"
ns_respond -status 200 -type $mimetype -string $__output -headers $headers

and added some params so you can give it a filename and whatever mimetype you like (if Excel's not your cup of meat).

Anyway so that's where I got to, and it seems to work. Can anybody see any disastrous errors with what I've done ? Ideally I'd like to change it a little more so that there's not redundant code now living in write_csv and send_file_to_browser (my new function's name), however I didn't want to change the file too much.

Cheers,

Jason =)

Collapse
Posted by Matthew Geddert on

The solution you have come up with is the same one I did a while back. One recommendation that I have for you, in order to ease upgradeability, is to create a package for your site named something like packages/mysite and put your new proc a /packages/mysite/tcl/mysite-customized-procs-init.tcl file, which would reference where the original procs are from and what you customized via a comment that is something like:

# From acs-templating/tcl/list-procs.tcl
# changed return make csv download as an
# attachment

ad_proc -public template::list::write_output {
...

This file would contain all procs you customize for your site. That way when you do an upgrade to a new version of acs-templating your customized proc would remain active. Packages are sourced alphabetically first with the -procs files then the -init files. Your custom proc will be initialized after all the procs files load since its an init file thus upgrading won't override your customized procs.

I used to document changes that I needed to make to a bunch of packages and every upgrade was a hassel since I had to redo all my changes - which got trickier and tricker the more i customized. Now, when I upgrade, I simply look in my /packages/mysite/tcl/mysite-customized-procs-init.tcl file and compare those procs to the ones that are in the upgraded packages. If there are differences, especially security changes, between the new ones and the procs I customized off of I choose what and how to modify my custom proc to match the changes made by the upgrade and my sites custom needs. Most of the procs I customized aren't changed with every upgrade so I don't have to do any customizations to those procs to keep running the way my users expect it to.

I hope this helps.