api06: simplify exception handling and add exception handling to the diff
[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     unless @trace
88       flash[:notice] = "OH NOES! Trace not found!"
89       redirect_to :controller => 'trace', :action => 'list'
90       return
91     end
92
93     @title = "Viewing trace #{@trace.name}"
94     if !@trace.visible?
95       render :nothing => true, :status => :not_found
96     elsif !@trace.public? and @trace.user.id != @user.id
97       render :nothing => true, :status => :forbidden
98     end
99   rescue ActiveRecord::RecordNotFound
100     flash[:notice] = "GPX file not found"
101     redirect_to :controller => 'trace', :action => 'list'
102   end
103
104   def create
105     logger.info(params[:trace][:gpx_file].class.name)
106     if params[:trace][:gpx_file].respond_to?(:read)
107       do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
108                 params[:trace][:description], params[:trace][:public])
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     else
117       @trace = Trace.new({:name => "Dummy",
118                          :tagstring => params[:trace][:tagstring],
119                          :description => params[:trace][:description],
120                          :public => params[:trace][:public],
121                          :inserted => false, :user => @user,
122                          :timestamp => Time.now})
123       @trace.valid?
124       @trace.errors.add(:gpx_file, "can't be blank")
125     end
126   end
127
128   def data
129     trace = Trace.find(params[:id])
130
131     if trace.visible? and (trace.public? or (@user and @user == trace.user))
132       if request.format == Mime::XML
133         send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment')
134       else
135         send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
136       end
137     else
138       render :nothing, :status => :not_found
139     end
140   rescue ActiveRecord::RecordNotFound
141     render :nothing => true, :status => :not_found
142   end
143
144   def edit
145     @trace = Trace.find(params[:id])
146
147     if @user and @trace.user == @user
148       if params[:trace]
149         @trace.description = params[:trace][:description]
150         @trace.tagstring = params[:trace][:tagstring]
151         if @trace.save
152           redirect_to :action => 'view'
153         end        
154       end
155     else
156       render :nothing, :status => :forbidden
157     end
158   rescue ActiveRecord::RecordNotFound
159     render :nothing => true, :status => :not_found
160   end
161
162   def delete
163     trace = Trace.find(params[:id])
164
165     if @user and trace.user == @user
166       if request.post? and trace.visible?
167         trace.visible = false
168         trace.save
169         flash[:notice] = 'Track scheduled for deletion'
170         redirect_to :controller => 'traces', :action => 'mine'
171       else
172         render :nothing, :status => :bad_request
173       end
174     else
175       render :nothing, :status => :forbidden
176     end
177   rescue ActiveRecord::RecordNotFound
178     render :nothing => true, :status => :not_found
179   end
180
181   def make_public
182     trace = Trace.find(params[:id])
183
184     if @user and trace.user == @user
185       if request.post? and !trace.public?
186         trace.public = true
187         trace.save
188         flash[:notice] = 'Track made public'
189         redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
190       else
191         render :nothing, :status => :bad_request
192       end
193     else
194       render :nothing, :status => :forbidden
195     end
196   rescue ActiveRecord::RecordNotFound
197     render :nothing => true, :status => :not_found
198   end
199
200   def georss
201     conditions = ["gpx_files.public = 1"]
202
203     if params[:display_name]
204       conditions[0] += " AND users.display_name = ?"
205       conditions << params[:display_name]
206     end
207
208     if params[:tag]
209       conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
210       conditions << params[:tag]
211     end
212
213     traces = Trace.find(:all, :include => :user, :conditions => conditions, 
214                         :order => "timestamp DESC", :limit => 20)
215
216     rss = OSM::GeoRSS.new
217
218     traces.each do |trace|
219       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)
220     end
221
222     render :text => rss.to_s, :content_type => "application/rss+xml"
223   end
224
225   def picture
226     trace = Trace.find(params[:id])
227
228     if trace.inserted?
229       if trace.public? or (@user and @user == trace.user)
230         send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
231       else
232         render :nothing, :status => :forbidden
233       end
234     else
235       render :nothing => true, :status => :not_found
236     end
237   rescue ActiveRecord::RecordNotFound
238     render :nothing => true, :status => :not_found
239   end
240
241   def icon
242     trace = Trace.find(params[:id])
243
244     if trace.inserted?
245       if trace.public? or (@user and @user == trace.user)
246         send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
247       else
248         render :nothing, :status => :forbidden
249       end
250     else
251       render :nothing => true, :status => :not_found
252     end
253   rescue ActiveRecord::RecordNotFound
254     render :nothing => true, :status => :not_found
255   end
256
257   def api_details
258     trace = Trace.find(params[:id])
259
260     if trace.public? or trace.user == @user
261       render :text => trace.to_xml.to_s, :content_type => "text/xml"
262     else
263       render :nothing => true, :status => :forbidden
264     end
265   rescue ActiveRecord::RecordNotFound
266     render :nothing => true, :status => :not_found
267   end
268
269   def api_data
270     trace = Trace.find(params[:id])
271
272     if trace.public? or trace.user == @user
273       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
274     else
275       render :nothing => true, :status => :forbidden
276     end
277   rescue ActiveRecord::RecordNotFound
278     render :nothing => true, :status => :not_found
279   end
280
281   def api_create
282     if request.post?
283       do_create(params[:file], params[:tags], params[:description], params[:public])
284
285       if @trace.id
286         render :text => @trace.id.to_s, :content_type => "text/plain"
287       elsif @trace.valid?
288         render :nothing => true, :status => :internal_server_error
289       else
290         render :nothing => true, :status => :bad_request
291       end
292     else
293       render :nothing => true, :status => :method_not_allowed
294     end
295   end
296
297   private
298
299   def do_create(file, tags, description, public)
300     name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
301     filename = "/tmp/#{rand}"
302
303     File.open(filename, "w") { |f| f.write(file.read) }
304
305     @trace = Trace.new({:name => name, :tagstring => tags,
306                        :description => description, :public => public})
307     @trace.inserted = false
308     @trace.user = @user
309     @trace.timestamp = Time.now
310
311     if @trace.save
312       FileUtils.mv(filename, @trace.trace_name)
313     else
314       FileUtils.rm_f(filename)
315     end
316   end
317
318 end