Provide a more sensible suggested filename when downloading traces.
[rails.git] / app / controllers / trace_controller.rb
1 class TraceController < ApplicationController
2   before_filter :authorize_web  
3   before_filter :authorize, :only => [:api_details, :api_data, :api_create]
4   layout 'site'
5  
6   # Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
7   #  target_user - if set, specifies the user to fetch traces for.  if not set will fetch all traces
8   #  paging_action - the action that will be linked back to from view
9   def list (target_user = nil, paging_action = 'list')
10     @title = 'public GPS traces'
11     @title += " tagged with #{params[:tag]}" if params[:tag]
12     @traces_per_page = 20
13     page_index = params[:page] ? params[:page].to_i - 1 : 0 # nice 1-based page -> 0-based page index
14
15     # from display name, pick up user id if one user's traces only
16     display_name = params[:display_name]
17     if target_user.nil? and display_name and display_name != ''
18       @paging_action = 'view'
19       @display_name = display_name
20       @title += " from #{@display_name}"
21       target_user = User.find(:first, :conditions => [ "display_name = ?", display_name])
22     end
23
24     opt = Hash.new
25     opt[:include] = [:user, :tags] # load users and tags from db at same time as traces
26
27     # four main cases:
28     # 1 - all traces, logged in = all public traces + all user's (i.e + all mine)
29     # 2 - all traces, not logged in = all public traces
30     # 3 - user's traces, logged in as same user = all user's traces 
31     # 4 - user's traces, not logged in as that user = all user's public traces
32     if target_user.nil? # all traces
33       if @user
34         conditions = ["(public = 1 OR user_id = ?)", @user.id] #1
35       else
36         conditions  = ["public = 1"] #2
37       end
38     else
39       if @user and @user.id == target_user.id
40         conditions = ["user_id = ?", @user.id] #3 (check vs user id, so no join + can't pick up non-public traces by changing name)
41       else
42         conditions = ["public = 1 AND user_id = ?", target_user.id] #4
43       end
44     end
45     conditions[0] += " AND users.display_name != ''" # users need to set display name before traces will be exposed
46     
47     opt[:order] = 'timestamp DESC'
48     if params[:tag]
49       @tag = params[:tag]
50       conditions[0] += " AND gpx_file_tags.tag = ?"
51       conditions << @tag;
52     end
53     
54     opt[:conditions] = conditions
55
56     # count traces using all options except limit
57     @max_trace = Trace.count(opt)
58     @max_page = Integer((@max_trace + 1) / @traces_per_page) 
59     
60     # last step before fetch - add paging options
61     opt[:limit] = @traces_per_page
62     if page_index > 0
63       opt[:offset] = @traces_per_page * page_index
64     end
65
66     @traces = Trace.find(:all , opt)
67     
68     # put together SET of tags across traces, for related links
69     tagset = Hash.new
70     if @traces
71       @traces.each do |trace|
72         trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
73         trace.tags.each do |tag|
74           tagset[tag.tag] = tag.tag
75         end
76       end
77     end
78     
79     # final helper vars for view
80     @display_name = display_name
81     @all_tags = tagset.values
82     @paging_action = paging_action # the action that paging requests should route back to, e.g. 'list' or 'mine'
83     @page = page_index + 1 # nice 1-based external page numbers
84   end
85
86   def mine
87     if @user
88       list(@user, 'mine') unless @user.nil?
89     else
90       redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri
91     end
92   end
93
94   def view
95     @trace = Trace.find(params[:id])
96     unless @trace.public
97       if @user
98         render :nothing, :status => :forbidden if @trace.user.id != @user.id
99       end
100     end
101   end
102
103   def create
104     name = params[:trace][:gpx_file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
105
106     do_create(name, params[:trace][:tagstring], params[:trace][:description], params[:trace][:public]) do |f|
107       f.write(params[:trace][:gpx_file].read)
108     end
109
110     if @trace.id
111       logger.info("id is #{@trace.id}")
112       flash[:notice] = "Your GPX file has been uploaded and is awaiting insertion in to the database. This will usually happen within half an hour, and an email will be sent to you on completion."
113
114       redirect_to :action => 'mine'
115     end
116   end
117
118   def data
119     trace = Trace.find(params[:id])
120     if trace and (trace.public? or (@user and @user == trace.user))
121       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
122     else
123       render :nothing, :status => :not_found
124     end
125   end
126
127   def make_public
128     trace = Trace.find(params[:id])
129     if @user and trace.user == @user and !trace.public
130       trace.public = true
131       trace.save
132       flash[:notice] = 'Track made public'
133       redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
134     end
135   end
136
137   def georss
138     traces = Trace.find(:all, :conditions => ['public = true'], :order => 'timestamp DESC', :limit => 20)
139
140     rss = OSM::GeoRSS.new
141
142     #def add(latitude=0, longitude=0, title_text='dummy title', url='http://www.example.com/', description_text='dummy description', timestamp=Time.now)
143     traces.each do |trace|
144       rss.add(trace.latitude, trace.longitude, trace.name, url_for({:controller => 'trace', :action => 'view', :id => trace.id, :display_name => trace.user.display_name}), "<img src='#{url_for({:controller => 'trace', :action => 'icon', :id => trace.id, :user_login => trace.user.display_name})}'> GPX file with #{trace.size} points from #{trace.user.display_name}", trace.timestamp)
145     end
146
147     render :text => rss.to_s, :content_type => "application/rss+xml"
148   end
149
150   def picture
151     begin
152       trace = Trace.find(params[:id])
153
154       if trace.public? or (@user and @user == trace.user)
155         send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
156       else
157         render :nothing, :status => :forbidden
158       end
159     rescue ActiveRecord::RecordNotFound
160       render :nothing => true, :status => :not_found
161     rescue
162       render :nothing => true, :status => :internal_server_error
163     end
164   end
165
166   def icon
167     begin
168       trace = Trace.find(params[:id])
169
170       if trace.public? or (@user and @user == trace.user)
171         send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
172       else
173         render :nothing, :status => :forbidden
174       end
175     rescue ActiveRecord::RecordNotFound
176       render :nothing => true, :status => :not_found
177     rescue
178       render :nothing => true, :status => :internal_server_error
179     end
180   end
181
182   def api_details
183     begin
184       trace = Trace.find(params[:id])
185
186       if trace.public? or trace.user == @user
187         render :text => trace.to_xml.to_s, :content_type => "text/xml"
188       else
189         render :nothing => true, :status => :forbidden
190       end
191     rescue ActiveRecord::RecordNotFound
192       render :nothing => true, :status => :not_found
193     rescue
194       render :nothing => true, :status => :internal_server_error
195     end
196   end
197
198   def api_data
199     render :action => 'data'
200   end
201
202   def api_create
203     do_create(params[:filename], params[:tags], params[:description], true) do |f|
204       f.write(request.raw_post)
205     end
206
207     if @trace.id
208       render :nothing => true
209     else
210       render :nothing => true, :status => :internal_server_error
211     end
212   end
213
214 private
215
216   def do_create(name, tags, description, public)
217     filename = "/tmp/#{rand}"
218
219     File.open(filename, "w") { |f| yield f }
220
221     @trace = Trace.new({:name => name, :tagstring => tags,
222                         :description => description, :public => public})
223     @trace.inserted = false
224     @trace.user = @user
225     @trace.timestamp = Time.now
226
227     if @trace.save
228       File.rename(filename, @trace.trace_name)
229     else
230       FileUtils.rm_f(filename)
231     end
232   end
233
234 end