1 class TraceController < ApplicationController
4 before_filter :authorize_web
5 before_filter :require_user, :only => [:mine, :create, :edit, :delete, :make_public]
6 before_filter :authorize, :only => [:api_details, :api_data, :api_create]
7 before_filter :check_database_readable, :except => [:api_details, :api_data, :api_create]
8 before_filter :check_database_writable, :only => [:create, :edit, :delete, :make_public]
9 before_filter :check_api_readable, :only => [:api_details, :api_data]
10 before_filter :check_api_writable, :only => [:api_create]
12 # Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
13 # target_user - if set, specifies the user to fetch traces for. if not set will fetch all traces
14 def list(target_user = nil, action = "list")
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.blank?
18 target_user = User.find(:first, :conditions => [ "visible = ? and display_name = ?", true, display_name])
20 @not_found_user = display_name
21 render :action => 'no_such_user', :status => :not_found
28 @title = "Public GPS traces"
29 elsif @user and @user == target_user
30 @title = "Your GPS traces"
32 @title = "Public GPS traces from #{target_user.display_name}"
35 @title += " tagged with #{params[:tag]}" if params[:tag]
38 # 1 - all traces, logged in = all public traces + all user's (i.e + all mine)
39 # 2 - all traces, not logged in = all public traces
40 # 3 - user's traces, logged in as same user = all user's traces
41 # 4 - user's traces, not logged in as that user = all user's public traces
42 if target_user.nil? # all traces
44 conditions = ["(gpx_files.public = ? OR gpx_files.user_id = ?)", true, @user.id] #1
46 conditions = ["gpx_files.public = ?", true] #2
49 if @user and @user == target_user
50 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)
52 conditions = ["gpx_files.public = ? AND gpx_files.user_id = ?", true, target_user.id] #4
59 files = Tracetag.find_all_by_tag(params[:tag]).collect { |tt| tt.gpx_id }
62 conditions[0] += " AND gpx_files.id IN (#{files.join(',')})"
64 conditions[0] += " AND 0 = 1"
68 conditions[0] += " AND gpx_files.visible = ?"
71 @trace_pages, @traces = paginate(:traces,
72 :include => [:user, :tags],
73 :conditions => conditions,
74 :order => "gpx_files.timestamp DESC",
77 # put together SET of tags across traces, for related links
80 @traces.each do |trace|
81 trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
82 trace.tags.each do |tag|
83 tagset[tag.tag] = tag.tag
88 # final helper vars for view
90 @display_name = target_user.display_name if target_user
91 @all_tags = tagset.values
95 # Load the preference of whether the user set the trace public the last time
97 if @user.preferences.find(:first, :conditions => {:k => "gps.trace.public", :v => "default"}).nil?
106 @trace = Trace.find(params[:id])
108 if @trace and @trace.visible? and
109 (@trace.public? or @trace.user == @user)
110 @title = "Viewing trace #{@trace.name}"
112 flash[:notice] = "Trace not found!"
113 redirect_to :controller => 'trace', :action => 'list'
115 rescue ActiveRecord::RecordNotFound
116 flash[:notice] = "Trace not found!"
117 redirect_to :controller => 'trace', :action => 'list'
122 logger.info(params[:trace][:gpx_file].class.name)
123 if params[:trace][:gpx_file].respond_to?(:read)
124 do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
125 params[:trace][:description], params[:trace][:public])
128 logger.info("id is #{@trace.id}")
129 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."
131 redirect_to :action => 'mine'
134 @trace = Trace.new({:name => "Dummy",
135 :tagstring => params[:trace][:tagstring],
136 :description => params[:trace][:description],
137 :public => params[:trace][:public],
138 :inserted => false, :user => @user,
139 :timestamp => Time.now.getutc})
141 @trace.errors.add(:gpx_file, "can't be blank")
147 trace = Trace.find(params[:id])
149 if trace.visible? and (trace.public? or (@user and @user == trace.user))
150 if request.format == Mime::XML
151 send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment')
153 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
156 render :nothing => true, :status => :not_found
158 rescue ActiveRecord::RecordNotFound
159 render :nothing => true, :status => :not_found
163 @trace = Trace.find(params[:id])
165 if @user and @trace.user == @user
167 @trace.description = params[:trace][:description]
168 @trace.tagstring = params[:trace][:tagstring]
170 redirect_to :action => 'view'
174 render :nothing => true, :status => :forbidden
176 rescue ActiveRecord::RecordNotFound
177 render :nothing => true, :status => :not_found
181 trace = Trace.find(params[:id])
183 if @user and trace.user == @user
184 if request.post? and trace.visible?
185 trace.visible = false
187 flash[:notice] = 'Track scheduled for deletion'
188 redirect_to :controller => 'traces', :action => 'mine'
190 render :nothing => true, :status => :bad_request
193 render :nothing => true, :status => :forbidden
195 rescue ActiveRecord::RecordNotFound
196 render :nothing => true, :status => :not_found
200 trace = Trace.find(params[:id])
202 if @user and trace.user == @user
203 if request.post? and !trace.public?
206 flash[:notice] = 'Track made public'
207 redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
209 render :nothing => true, :status => :bad_request
212 render :nothing => true, :status => :forbidden
214 rescue ActiveRecord::RecordNotFound
215 render :nothing => true, :status => :not_found
219 conditions = ["gpx_files.public = ?", true]
221 if params[:display_name]
222 conditions[0] += " AND users.display_name = ?"
223 conditions << params[:display_name]
227 conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
228 conditions << params[:tag]
231 traces = Trace.find(:all, :include => :user, :conditions => conditions,
232 :order => "timestamp DESC", :limit => 20)
234 rss = OSM::GeoRSS.new
236 traces.each do |trace|
237 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)
240 render :text => rss.to_s, :content_type => "application/rss+xml"
244 trace = Trace.find(params[:id])
247 if trace.public? or (@user and @user == trace.user)
248 expires_in 7.days, :private => !trace.public, :public => trace.public
249 send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
251 render :nothing => true, :status => :forbidden
254 render :nothing => true, :status => :not_found
256 rescue ActiveRecord::RecordNotFound
257 render :nothing => true, :status => :not_found
261 trace = Trace.find(params[:id])
264 if trace.public? or (@user and @user == trace.user)
265 expires_in 7.days, :private => !trace.public, :public => trace.public
266 send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
268 render :nothing => true, :status => :forbidden
271 render :nothing => true, :status => :not_found
273 rescue ActiveRecord::RecordNotFound
274 render :nothing => true, :status => :not_found
278 trace = Trace.find(params[:id])
280 if trace.public? or trace.user == @user
281 render :text => trace.to_xml.to_s, :content_type => "text/xml"
283 render :nothing => true, :status => :forbidden
285 rescue ActiveRecord::RecordNotFound
286 render :nothing => true, :status => :not_found
290 trace = Trace.find(params[:id])
292 if trace.public? or trace.user == @user
293 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
295 render :nothing => true, :status => :forbidden
297 rescue ActiveRecord::RecordNotFound
298 render :nothing => true, :status => :not_found
303 tags = params[:tags] || ""
304 description = params[:description] || ""
305 pub = params[:public] || false
307 if params[:file].respond_to?(:read)
308 do_create(params[:file], tags, description, pub)
311 render :text => @trace.id.to_s, :content_type => "text/plain"
313 render :nothing => true, :status => :internal_server_error
315 render :nothing => true, :status => :bad_request
318 render :nothing => true, :status => :bad_request
321 render :nothing => true, :status => :method_not_allowed
327 def do_create(file, tags, description, public)
328 # Sanitise the user's filename
329 name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
331 # Get a temporary filename...
332 filename = "/tmp/#{rand}"
334 # ...and save the uploaded file to that location
335 File.open(filename, "w") { |f| f.write(file.read) }
337 # Create the trace object, falsely marked as already
338 # inserted to stop the import daemon trying to load it
342 :description => description,
346 :timestamp => Time.now.getutc
349 # Save the trace object
351 # Rename the temporary file to the final name
352 FileUtils.mv(filename, @trace.trace_name)
354 # Clear the inserted flag to make the import daemon load the trace
355 @trace.inserted = false
358 # Remove the file as we have failed to update the database
359 FileUtils.rm_f(filename)
362 # Finally save whether the user marked the trace as being public
364 if @user.trace_public_default.nil?
365 @user.preferences.create(:k => "gps.trace.public", :v => "default")
368 pref = @user.trace_public_default
369 pref.destroy unless pref.nil?