Tidy up handling of unknown 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   def list(target_user = nil, action = "list")
9     # from display name, pick up user id if one user's traces only
10     display_name = params[:display_name]
11     if target_user.nil? and !display_name.blank?
12       target_user = User.find(:first, :conditions => [ "display_name = ?", display_name])
13     end
14
15     # set title
16     if target_user.nil?
17       @title = "Public GPS traces"
18     elsif @user and @user.id == target_user.id
19       @title = "Your GPS traces"
20     else
21       @title = "Public GPS traces from #{target_user.display_name}"
22     end
23
24     @title += " tagged with #{params[:tag]}" if params[:tag]
25
26     # four main cases:
27     # 1 - all traces, logged in = all public traces + all user's (i.e + all mine)
28     # 2 - all traces, not logged in = all public traces
29     # 3 - user's traces, logged in as same user = all user's traces 
30     # 4 - user's traces, not logged in as that user = all user's public traces
31     if target_user.nil? # all traces
32       if @user
33         conditions = ["(gpx_files.public = 1 OR gpx_files.user_id = ?)", @user.id] #1
34       else
35         conditions  = ["gpx_files.public = 1"] #2
36       end
37     else
38       if @user and @user.id == target_user.id
39         conditions = ["gpx_files.user_id = ?", @user.id] #3 (check vs user id, so no join + can't pick up non-public traces by changing name)
40       else
41         conditions = ["gpx_files.public = 1 AND gpx_files.user_id = ?", target_user.id] #4
42       end
43     end
44     
45     if params[:tag]
46       @tag = params[:tag]
47       conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
48       conditions << @tag
49     end
50     
51     conditions[0] += " AND gpx_files.visible = 1"
52
53     @trace_pages, @traces = paginate(:traces,
54                                      :include => [:user, :tags],
55                                      :conditions => conditions,
56                                      :order => "gpx_files.timestamp DESC",
57                                      :per_page => 20)
58
59     # put together SET of tags across traces, for related links
60     tagset = Hash.new
61     if @traces
62       @traces.each do |trace|
63         trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
64         trace.tags.each do |tag|
65           tagset[tag.tag] = tag.tag
66         end
67       end
68     end
69     
70     # final helper vars for view
71     @action = action
72     @display_name = target_user.display_name if target_user
73     @all_tags = tagset.values
74   end
75
76   def mine
77     if @user
78       list(@user, "mine") unless @user.nil?
79     else
80       redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri
81     end
82   end
83
84   def view
85     @trace = Trace.find(params[:id])
86
87     if @trace and @trace.visible? and
88        (@trace.public? or @trace.user.id == @user.id)
89       @title = "Viewing trace #{@trace.name}"
90     else
91       flash[:notice] = "Trace not found!"
92       redirect_to :controller => 'trace', :action => 'list'
93     end
94   rescue ActiveRecord::RecordNotFound
95     flash[:notice] = "Trace not found!"
96     redirect_to :controller => 'trace', :action => 'list'
97   end
98
99   def create
100     logger.info(params[:trace][:gpx_file].class.name)
101     if params[:trace][:gpx_file].respond_to?(:read)
102       do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
103                 params[:trace][:description], params[:trace][:public])
104
105       if @trace.id
106         logger.info("id is #{@trace.id}")
107         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."
108
109         redirect_to :action => 'mine'
110       end
111     else
112       @trace = Trace.new({:name => "Dummy",
113                           :tagstring => params[:trace][:tagstring],
114                           :description => params[:trace][:description],
115                           :public => params[:trace][:public],
116                           :inserted => false, :user => @user,
117                           :timestamp => Time.now})
118       @trace.valid?
119       @trace.errors.add(:gpx_file, "can't be blank")
120     end
121   end
122
123   def data
124     trace = Trace.find(params[:id])
125
126     if trace.visible? and (trace.public? or (@user and @user == trace.user))
127       if request.format == Mime::XML
128         send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment')
129       else
130         send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
131       end
132     else
133       render :nothing, :status => :not_found
134     end
135   rescue ActiveRecord::RecordNotFound
136     render :nothing => true, :status => :not_found
137   end
138
139   def edit
140     @trace = Trace.find(params[:id])
141
142     if @user and @trace.user == @user
143       if params[:trace]
144         @trace.description = params[:trace][:description]
145         @trace.tagstring = params[:trace][:tagstring]
146         if @trace.save
147           redirect_to :action => 'view'
148         end        
149       end
150     else
151       render :nothing, :status => :forbidden
152     end
153   rescue ActiveRecord::RecordNotFound
154     render :nothing => true, :status => :not_found
155   end
156
157   def delete
158     trace = Trace.find(params[:id])
159
160     if @user and trace.user == @user
161       if request.post? and trace.visible?
162         trace.visible = false
163         trace.save
164         flash[:notice] = 'Track scheduled for deletion'
165         redirect_to :controller => 'traces', :action => 'mine'
166       else
167         render :nothing, :status => :bad_request
168       end
169     else
170       render :nothing, :status => :forbidden
171     end
172   rescue ActiveRecord::RecordNotFound
173     render :nothing => true, :status => :not_found
174   end
175
176   def make_public
177     trace = Trace.find(params[:id])
178
179     if @user and trace.user == @user
180       if request.post? and !trace.public?
181         trace.public = true
182         trace.save
183         flash[:notice] = 'Track made public'
184         redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
185       else
186         render :nothing, :status => :bad_request
187       end
188     else
189       render :nothing, :status => :forbidden
190     end
191   rescue ActiveRecord::RecordNotFound
192     render :nothing => true, :status => :not_found
193   end
194
195   def georss
196     conditions = ["gpx_files.public = 1"]
197
198     if params[:display_name]
199       conditions[0] += " AND users.display_name = ?"
200       conditions << params[:display_name]
201     end
202
203     if params[:tag]
204       conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
205       conditions << params[:tag]
206     end
207
208     traces = Trace.find(:all, :include => :user, :conditions => conditions, 
209                         :order => "timestamp DESC", :limit => 20)
210
211     rss = OSM::GeoRSS.new
212
213     traces.each do |trace|
214       rss.add(trace.latitude, trace.longitude, trace.name, trace.user.display_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)
215     end
216
217     render :text => rss.to_s, :content_type => "application/rss+xml"
218   end
219
220   def picture
221     trace = Trace.find(params[:id])
222
223     if trace.inserted?
224       if trace.public? or (@user and @user == trace.user)
225         send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
226       else
227         render :nothing, :status => :forbidden
228       end
229     else
230       render :nothing => true, :status => :not_found
231     end
232   rescue ActiveRecord::RecordNotFound
233     render :nothing => true, :status => :not_found
234   end
235
236   def icon
237     trace = Trace.find(params[:id])
238
239     if trace.inserted?
240       if trace.public? or (@user and @user == trace.user)
241         send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
242       else
243         render :nothing, :status => :forbidden
244       end
245     else
246       render :nothing => true, :status => :not_found
247     end
248   rescue ActiveRecord::RecordNotFound
249     render :nothing => true, :status => :not_found
250   end
251
252   def api_details
253     trace = Trace.find(params[:id])
254
255     if trace.public? or trace.user == @user
256       render :text => trace.to_xml.to_s, :content_type => "text/xml"
257     else
258       render :nothing => true, :status => :forbidden
259     end
260   rescue ActiveRecord::RecordNotFound
261     render :nothing => true, :status => :not_found
262   end
263
264   def api_data
265     trace = Trace.find(params[:id])
266
267     if trace.public? or trace.user == @user
268       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
269     else
270       render :nothing => true, :status => :forbidden
271     end
272   rescue ActiveRecord::RecordNotFound
273     render :nothing => true, :status => :not_found
274   end
275
276   def api_create
277     if request.post?
278       do_create(params[:file], params[:tags], params[:description], params[:public])
279
280       if @trace.id
281         render :text => @trace.id.to_s, :content_type => "text/plain"
282       elsif @trace.valid?
283         render :nothing => true, :status => :internal_server_error
284       else
285         render :nothing => true, :status => :bad_request
286       end
287     else
288       render :nothing => true, :status => :method_not_allowed
289     end
290   end
291
292 private
293
294   def do_create(file, tags, description, public)
295     name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
296     filename = "/tmp/#{rand}"
297
298     File.open(filename, "w") { |f| f.write(file.read) }
299
300     @trace = Trace.new({:name => name, :tagstring => tags,
301                         :description => description, :public => public})
302     @trace.inserted = false
303     @trace.user = @user
304     @trace.timestamp = Time.now
305
306     if @trace.save
307       FileUtils.mv(filename, @trace.trace_name)
308     else
309       FileUtils.rm_f(filename)
310     end
311   end
312
313 end