Attaching files to new messages
I really love Basecamp, but there’s this little issue with attached files that keeps biting me. The problem is that the url of the file changes after you’ve saved a new message.
So, I’m working on a design, and I’d like to show it to my client. After I upload the file, I copy the url by right clicking its link. Then I paste the url with formatting codes that include the image in the message body:

After I post the message, the image is missing:

So I click ‘Edit’ to find out what I did wrong. As you can see, the url for the attached file has changed. I copy and paste the new url in the message body:

And now it works:

What probably happens is that uploaded files are saved to a temporary location when you’re working on a new message. You have to post the message before a message object is created. Only after a message object has been created, is there a message id where attachment objects can be linked to. The problem is that you simply can’t link the uploaded files to a message that doesn’t yet exist.
Please note that this is a completely sensible solution for this kind of application; I’m probably one of the very few people who likes to include images inside the message body.
When you write an application for managing the content of a website this is much more of an issue. The people who publish articles on their website are very likely to want to include images.
For simple systems, we often take the easy route. You first have to save the article as a draft:

Only then can you attach and include images:

For more complex systems, we’ve solved this problem by saving a record for the article to the database right after you click ‘Add article’ so that we always have an article id to link the attachments to.
These articles have a status attribute that is set to ‘new’. The status is changed to ‘draft’ or ‘published’ after you save the article. If you don’t save a new article, it gets garbage collected within a few days.
This is what the relevant parts of the article model look like:
class Article < ActiveRecord::Base
has_many :assets, :order => :position, :dependent => true
def self.collect_garbage
resources = Resource.find :all, :conditions => "status = 'new' AND
created_at < DATE_SUB(NOW(), INTERVAL 7 DAY)"
resources.each {|resource| resource.destroy}
end
end
Article.collect_garbage is called in the list action of the article controller, but you could also use a cronjob of find some other way to call it regularly.
This is what the relevant parts of the article controller look like:
def new
@article = Article.new
@article.save
redirect_to :action => 'edit', :id => @article.id
end
def edit
@article = Article.find(params[:id])
if request.post?
@article.attributes = params[:article]
if params[:upload]
# handle asset upload
asset = @article.assets.build
if asset.handle_upload(params['asset'])
asset.save
flash['highlight'] = "asset#{asset.id}"
end
else
# handle article submit
if @article.save
flash['highlight'] = "article#{@article.id}"
redirect_to :action => 'list'
return false
end
end
end
end
You can view an example of how this works (Quicktime).
Add your comment
In order to fight spam on this blog, posting comments from a browser without javascript is currently not supported.
Subscribe
Comments