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