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 }
60 conditions[0] += " AND gpx_files.id IN (#{files.join(',')})"
63 conditions[0] += " AND gpx_files.visible = ?"
66 @trace_pages, @traces = paginate(:traces,
67 :include => [:user, :tags],
68 :conditions => conditions,
69 :order => "gpx_files.timestamp DESC",
72 # put together SET of tags across traces, for related links
75 @traces.each do |trace|
76 trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
77 trace.tags.each do |tag|
78 tagset[tag.tag] = tag.tag
83 # final helper vars for view
85 @display_name = target_user.display_name if target_user
86 @all_tags = tagset.values
94 @trace = Trace.find(params[:id])
96 if @trace and @trace.visible? and
97 (@trace.public? or @trace.user == @user)
98 @title = "Viewing trace #{@trace.name}"
100 flash[:notice] = "Trace not found!"
101 redirect_to :controller => 'trace', :action => 'list'
103 rescue ActiveRecord::RecordNotFound
104 flash[:notice] = "Trace not found!"
105 redirect_to :controller => 'trace', :action => 'list'
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])
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."
119 redirect_to :action => 'mine'
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.getutc})
129 @trace.errors.add(:gpx_file, "can't be blank")
135 trace = Trace.find(params[:id])
137 if trace.visible? and (trace.public? or (@user and @user == trace.user))
138 if request.format == Mime::XML
139 send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment')
141 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
144 render :nothing => true, :status => :not_found
146 rescue ActiveRecord::RecordNotFound
147 render :nothing => true, :status => :not_found
151 @trace = Trace.find(params[:id])
153 if @user and @trace.user == @user
155 @trace.description = params[:trace][:description]
156 @trace.tagstring = params[:trace][:tagstring]
158 redirect_to :action => 'view'
162 render :nothing => true, :status => :forbidden
164 rescue ActiveRecord::RecordNotFound
165 render :nothing => true, :status => :not_found
169 trace = Trace.find(params[:id])
171 if @user and trace.user == @user
172 if request.post? and trace.visible?
173 trace.visible = false
175 flash[:notice] = 'Track scheduled for deletion'
176 redirect_to :controller => 'traces', :action => 'mine'
178 render :nothing => true, :status => :bad_request
181 render :nothing => true, :status => :forbidden
183 rescue ActiveRecord::RecordNotFound
184 render :nothing => true, :status => :not_found
188 trace = Trace.find(params[:id])
190 if @user and trace.user == @user
191 if request.post? and !trace.public?
194 flash[:notice] = 'Track made public'
195 redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
197 render :nothing => true, :status => :bad_request
200 render :nothing => true, :status => :forbidden
202 rescue ActiveRecord::RecordNotFound
203 render :nothing => true, :status => :not_found
207 conditions = ["gpx_files.public = ?", true]
209 if params[:display_name]
210 conditions[0] += " AND users.display_name = ?"
211 conditions << params[:display_name]
215 conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
216 conditions << params[:tag]
219 traces = Trace.find(:all, :include => :user, :conditions => conditions,
220 :order => "timestamp DESC", :limit => 20)
222 rss = OSM::GeoRSS.new
224 traces.each do |trace|
225 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)
228 render :text => rss.to_s, :content_type => "application/rss+xml"
232 trace = Trace.find(params[:id])
235 if trace.public? or (@user and @user == trace.user)
236 send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
238 render :nothing => true, :status => :forbidden
241 render :nothing => true, :status => :not_found
243 rescue ActiveRecord::RecordNotFound
244 render :nothing => true, :status => :not_found
248 trace = Trace.find(params[:id])
251 if trace.public? or (@user and @user == trace.user)
252 send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
254 render :nothing => true, :status => :forbidden
257 render :nothing => true, :status => :not_found
259 rescue ActiveRecord::RecordNotFound
260 render :nothing => true, :status => :not_found
264 trace = Trace.find(params[:id])
266 if trace.public? or trace.user == @user
267 render :text => trace.to_xml.to_s, :content_type => "text/xml"
269 render :nothing => true, :status => :forbidden
271 rescue ActiveRecord::RecordNotFound
272 render :nothing => true, :status => :not_found
276 trace = Trace.find(params[:id])
278 if trace.public? or trace.user == @user
279 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
281 render :nothing => true, :status => :forbidden
283 rescue ActiveRecord::RecordNotFound
284 render :nothing => true, :status => :not_found
289 tags = params[:tags] || ""
290 description = params[:description] || ""
291 pub = params[:public] || false
293 if params[:file].respond_to?(:read)
294 do_create(params[:file], tags, description, pub)
297 render :text => @trace.id.to_s, :content_type => "text/plain"
299 render :nothing => true, :status => :internal_server_error
301 render :nothing => true, :status => :bad_request
304 render :nothing => true, :status => :bad_request
307 render :nothing => true, :status => :method_not_allowed
313 def do_create(file, tags, description, public)
314 # Sanitise the user's filename
315 name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
317 # Get a temporary filename...
318 filename = "/tmp/#{rand}"
320 # ...and save the uploaded file to that location
321 File.open(filename, "w") { |f| f.write(file.read) }
323 # Create the trace object, falsely marked as already
324 # inserted to stop the import daemon trying to load it
328 :description => description,
332 :timestamp => Time.now.getutc
335 # Save the trace object
337 # Rename the temporary file to the final name
338 FileUtils.mv(filename, @trace.trace_name)
340 # Clear the inserted flag to make the import daemon load the trace
341 @trace.inserted = false
344 # Remove the file as we have failed to update the database
345 FileUtils.rm_f(filename)
348 # Finally save whether the user marked the trace as being public
350 if @user.trace_public_default.nil?
351 @user.preferences.create(:k => "gps.trace.public", :v => "default")
354 pref = @user.trace_public_default
355 pref.destroy unless pref.nil?