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(',')})"
66 conditions[0] += " AND gpx_files.visible = ?"
69 @trace_pages, @traces = paginate(:traces,
70 :include => [:user, :tags],
71 :conditions => conditions,
72 :order => "gpx_files.timestamp DESC",
75 # put together SET of tags across traces, for related links
78 @traces.each do |trace|
79 trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
80 trace.tags.each do |tag|
81 tagset[tag.tag] = tag.tag
86 # final helper vars for view
88 @display_name = target_user.display_name if target_user
89 @all_tags = tagset.values
97 @trace = Trace.find(params[:id])
99 if @trace and @trace.visible? and
100 (@trace.public? or @trace.user == @user)
101 @title = "Viewing trace #{@trace.name}"
103 flash[:notice] = "Trace not found!"
104 redirect_to :controller => 'trace', :action => 'list'
106 rescue ActiveRecord::RecordNotFound
107 flash[:notice] = "Trace not found!"
108 redirect_to :controller => 'trace', :action => 'list'
113 logger.info(params[:trace][:gpx_file].class.name)
114 if params[:trace][:gpx_file].respond_to?(:read)
115 do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
116 params[:trace][:description], params[:trace][:public])
119 logger.info("id is #{@trace.id}")
120 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."
122 redirect_to :action => 'mine'
125 @trace = Trace.new({:name => "Dummy",
126 :tagstring => params[:trace][:tagstring],
127 :description => params[:trace][:description],
128 :public => params[:trace][:public],
129 :inserted => false, :user => @user,
130 :timestamp => Time.now.getutc})
132 @trace.errors.add(:gpx_file, "can't be blank")
138 trace = Trace.find(params[:id])
140 if trace.visible? and (trace.public? or (@user and @user == trace.user))
141 if request.format == Mime::XML
142 send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment')
144 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
147 render :nothing => true, :status => :not_found
149 rescue ActiveRecord::RecordNotFound
150 render :nothing => true, :status => :not_found
154 @trace = Trace.find(params[:id])
156 if @user and @trace.user == @user
158 @trace.description = params[:trace][:description]
159 @trace.tagstring = params[:trace][:tagstring]
161 redirect_to :action => 'view'
165 render :nothing => true, :status => :forbidden
167 rescue ActiveRecord::RecordNotFound
168 render :nothing => true, :status => :not_found
172 trace = Trace.find(params[:id])
174 if @user and trace.user == @user
175 if request.post? and trace.visible?
176 trace.visible = false
178 flash[:notice] = 'Track scheduled for deletion'
179 redirect_to :controller => 'traces', :action => 'mine'
181 render :nothing => true, :status => :bad_request
184 render :nothing => true, :status => :forbidden
186 rescue ActiveRecord::RecordNotFound
187 render :nothing => true, :status => :not_found
191 trace = Trace.find(params[:id])
193 if @user and trace.user == @user
194 if request.post? and !trace.public?
197 flash[:notice] = 'Track made public'
198 redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
200 render :nothing => true, :status => :bad_request
203 render :nothing => true, :status => :forbidden
205 rescue ActiveRecord::RecordNotFound
206 render :nothing => true, :status => :not_found
210 conditions = ["gpx_files.public = ?", true]
212 if params[:display_name]
213 conditions[0] += " AND users.display_name = ?"
214 conditions << params[:display_name]
218 conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
219 conditions << params[:tag]
222 traces = Trace.find(:all, :include => :user, :conditions => conditions,
223 :order => "timestamp DESC", :limit => 20)
225 rss = OSM::GeoRSS.new
227 traces.each do |trace|
228 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)
231 render :text => rss.to_s, :content_type => "application/rss+xml"
235 trace = Trace.find(params[:id])
238 if trace.public? or (@user and @user == trace.user)
239 expires_in 7.days, :private => !trace.public, :public => trace.public
240 send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
242 render :nothing => true, :status => :forbidden
245 render :nothing => true, :status => :not_found
247 rescue ActiveRecord::RecordNotFound
248 render :nothing => true, :status => :not_found
252 trace = Trace.find(params[:id])
255 if trace.public? or (@user and @user == trace.user)
256 expires_in 7.days, :private => !trace.public, :public => trace.public
257 send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
259 render :nothing => true, :status => :forbidden
262 render :nothing => true, :status => :not_found
264 rescue ActiveRecord::RecordNotFound
265 render :nothing => true, :status => :not_found
269 trace = Trace.find(params[:id])
271 if trace.public? or trace.user == @user
272 render :text => trace.to_xml.to_s, :content_type => "text/xml"
274 render :nothing => true, :status => :forbidden
276 rescue ActiveRecord::RecordNotFound
277 render :nothing => true, :status => :not_found
281 trace = Trace.find(params[:id])
283 if trace.public? or trace.user == @user
284 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
286 render :nothing => true, :status => :forbidden
288 rescue ActiveRecord::RecordNotFound
289 render :nothing => true, :status => :not_found
294 tags = params[:tags] || ""
295 description = params[:description] || ""
296 pub = params[:public] || false
298 if params[:file].respond_to?(:read)
299 do_create(params[:file], tags, description, pub)
302 render :text => @trace.id.to_s, :content_type => "text/plain"
304 render :nothing => true, :status => :internal_server_error
306 render :nothing => true, :status => :bad_request
309 render :nothing => true, :status => :bad_request
312 render :nothing => true, :status => :method_not_allowed
318 def do_create(file, tags, description, public)
319 # Sanitise the user's filename
320 name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
322 # Get a temporary filename...
323 filename = "/tmp/#{rand}"
325 # ...and save the uploaded file to that location
326 File.open(filename, "w") { |f| f.write(file.read) }
328 # Create the trace object, falsely marked as already
329 # inserted to stop the import daemon trying to load it
333 :description => description,
337 :timestamp => Time.now.getutc
340 # Save the trace object
342 # Rename the temporary file to the final name
343 FileUtils.mv(filename, @trace.trace_name)
345 # Clear the inserted flag to make the import daemon load the trace
346 @trace.inserted = false
349 # Remove the file as we have failed to update the database
350 FileUtils.rm_f(filename)
353 # Finally save whether the user marked the trace as being public
355 if @user.trace_public_default.nil?
356 @user.preferences.create(:k => "gps.trace.public", :v => "default")
359 pref = @user.trace_public_default
360 pref.destroy unless pref.nil?