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
99 @trace = Trace.find(params[:id])
101 if @trace and @trace.visible? and
102 (@trace.public? or @trace.user == @user)
103 @title = "Viewing trace #{@trace.name}"
105 flash[:notice] = "Trace not found!"
106 redirect_to :controller => 'trace', :action => 'list'
108 rescue ActiveRecord::RecordNotFound
109 flash[:notice] = "Trace not found!"
110 redirect_to :controller => 'trace', :action => 'list'
115 logger.info(params[:trace][:gpx_file].class.name)
116 if params[:trace][:gpx_file].respond_to?(:read)
117 do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
118 params[:trace][:description], params[:trace][:public])
121 logger.info("id is #{@trace.id}")
122 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."
124 redirect_to :action => 'mine'
127 @trace = Trace.new({:name => "Dummy",
128 :tagstring => params[:trace][:tagstring],
129 :description => params[:trace][:description],
130 :public => params[:trace][:public],
131 :inserted => false, :user => @user,
132 :timestamp => Time.now.getutc})
134 @trace.errors.add(:gpx_file, "can't be blank")
140 trace = Trace.find(params[:id])
142 if trace.visible? and (trace.public? or (@user and @user == trace.user))
143 if request.format == Mime::XML
144 send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment')
146 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
149 render :nothing => true, :status => :not_found
151 rescue ActiveRecord::RecordNotFound
152 render :nothing => true, :status => :not_found
156 @trace = Trace.find(params[:id])
158 if @user and @trace.user == @user
160 @trace.description = params[:trace][:description]
161 @trace.tagstring = params[:trace][:tagstring]
163 redirect_to :action => 'view'
167 render :nothing => true, :status => :forbidden
169 rescue ActiveRecord::RecordNotFound
170 render :nothing => true, :status => :not_found
174 trace = Trace.find(params[:id])
176 if @user and trace.user == @user
177 if request.post? and trace.visible?
178 trace.visible = false
180 flash[:notice] = 'Track scheduled for deletion'
181 redirect_to :controller => 'traces', :action => 'mine'
183 render :nothing => true, :status => :bad_request
186 render :nothing => true, :status => :forbidden
188 rescue ActiveRecord::RecordNotFound
189 render :nothing => true, :status => :not_found
193 trace = Trace.find(params[:id])
195 if @user and trace.user == @user
196 if request.post? and !trace.public?
199 flash[:notice] = 'Track made public'
200 redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
202 render :nothing => true, :status => :bad_request
205 render :nothing => true, :status => :forbidden
207 rescue ActiveRecord::RecordNotFound
208 render :nothing => true, :status => :not_found
212 conditions = ["gpx_files.public = ?", true]
214 if params[:display_name]
215 conditions[0] += " AND users.display_name = ?"
216 conditions << params[:display_name]
220 conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
221 conditions << params[:tag]
224 traces = Trace.find(:all, :include => :user, :conditions => conditions,
225 :order => "timestamp DESC", :limit => 20)
227 rss = OSM::GeoRSS.new
229 traces.each do |trace|
230 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)
233 render :text => rss.to_s, :content_type => "application/rss+xml"
237 trace = Trace.find(params[:id])
240 if trace.public? or (@user and @user == trace.user)
241 expires_in 7.days, :private => !trace.public, :public => trace.public
242 send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
244 render :nothing => true, :status => :forbidden
247 render :nothing => true, :status => :not_found
249 rescue ActiveRecord::RecordNotFound
250 render :nothing => true, :status => :not_found
254 trace = Trace.find(params[:id])
257 if trace.public? or (@user and @user == trace.user)
258 expires_in 7.days, :private => !trace.public, :public => trace.public
259 send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
261 render :nothing => true, :status => :forbidden
264 render :nothing => true, :status => :not_found
266 rescue ActiveRecord::RecordNotFound
267 render :nothing => true, :status => :not_found
271 trace = Trace.find(params[:id])
273 if trace.public? or trace.user == @user
274 render :text => trace.to_xml.to_s, :content_type => "text/xml"
276 render :nothing => true, :status => :forbidden
278 rescue ActiveRecord::RecordNotFound
279 render :nothing => true, :status => :not_found
283 trace = Trace.find(params[:id])
285 if trace.public? or trace.user == @user
286 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
288 render :nothing => true, :status => :forbidden
290 rescue ActiveRecord::RecordNotFound
291 render :nothing => true, :status => :not_found
296 tags = params[:tags] || ""
297 description = params[:description] || ""
298 pub = params[:public] || false
300 if params[:file].respond_to?(:read)
301 do_create(params[:file], tags, description, pub)
304 render :text => @trace.id.to_s, :content_type => "text/plain"
306 render :nothing => true, :status => :internal_server_error
308 render :nothing => true, :status => :bad_request
311 render :nothing => true, :status => :bad_request
314 render :nothing => true, :status => :method_not_allowed
320 def do_create(file, tags, description, public)
321 # Sanitise the user's filename
322 name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
324 # Get a temporary filename...
325 filename = "/tmp/#{rand}"
327 # ...and save the uploaded file to that location
328 File.open(filename, "w") { |f| f.write(file.read) }
330 # Create the trace object, falsely marked as already
331 # inserted to stop the import daemon trying to load it
335 :description => description,
339 :timestamp => Time.now.getutc
342 # Save the trace object
344 # Rename the temporary file to the final name
345 FileUtils.mv(filename, @trace.trace_name)
347 # Clear the inserted flag to make the import daemon load the trace
348 @trace.inserted = false
351 # Remove the file as we have failed to update the database
352 FileUtils.rm_f(filename)
355 # Finally save whether the user marked the trace as being public
357 if @user.trace_public_default.nil?
358 @user.preferences.create(:k => "gps.trace.public", :v => "default")
361 pref = @user.trace_public_default
362 pref.destroy unless pref.nil?