1 class TraceController < ApplicationController
4 before_filter :authorize_web
5 before_filter :set_locale
6 before_filter :require_user, :only => [:mine, :create, :edit, :delete, :make_public]
7 before_filter :authorize, :only => [:api_details, :api_data, :api_create]
8 before_filter :check_database_readable, :except => [:api_details, :api_data, :api_create]
9 before_filter :check_database_writable, :only => [:create, :edit, :delete, :make_public]
10 before_filter :check_api_readable, :only => [:api_details, :api_data]
11 before_filter :check_api_writable, :only => [:api_create]
13 # Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
14 # target_user - if set, specifies the user to fetch traces for. if not set will fetch all traces
15 def list(target_user = nil, action = "list")
16 # from display name, pick up user id if one user's traces only
17 display_name = params[:display_name]
18 if target_user.nil? and !display_name.blank?
19 target_user = User.find(:first, :conditions => [ "visible = ? and display_name = ?", true, display_name])
21 @not_found_user = display_name
22 render :action => 'no_such_user', :status => :not_found
29 @title = "Public GPS traces"
30 elsif @user and @user == target_user
31 @title = "Your GPS traces"
33 @title = "Public GPS traces from #{target_user.display_name}"
36 @title += " tagged with #{params[:tag]}" if params[:tag]
39 # 1 - all traces, logged in = all public traces + all user's (i.e + all mine)
40 # 2 - all traces, not logged in = all public traces
41 # 3 - user's traces, logged in as same user = all user's traces
42 # 4 - user's traces, not logged in as that user = all user's public traces
43 if target_user.nil? # all traces
45 conditions = ["(gpx_files.public = ? OR gpx_files.user_id = ?)", true, @user.id] #1
47 conditions = ["gpx_files.public = ?", true] #2
50 if @user and @user == target_user
51 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)
53 conditions = ["gpx_files.public = ? AND gpx_files.user_id = ?", true, target_user.id] #4
60 files = Tracetag.find_all_by_tag(params[:tag]).collect { |tt| tt.gpx_id }
63 conditions[0] += " AND gpx_files.id IN (#{files.join(',')})"
65 conditions[0] += " AND 0 = 1"
69 conditions[0] += " AND gpx_files.visible = ?"
72 @trace_pages, @traces = paginate(:traces,
73 :include => [:user, :tags],
74 :conditions => conditions,
75 :order => "gpx_files.timestamp DESC",
78 # put together SET of tags across traces, for related links
81 @traces.each do |trace|
82 trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
83 trace.tags.each do |tag|
84 tagset[tag.tag] = tag.tag
89 # final helper vars for view
91 @display_name = target_user.display_name if target_user
92 @all_tags = tagset.values
96 # Load the preference of whether the user set the trace public the last time
98 if @user.preferences.find(:first, :conditions => {:k => "gps.trace.public", :v => "default"}).nil?
107 @trace = Trace.find(params[:id])
109 if @trace and @trace.visible? and
110 (@trace.public? or @trace.user == @user)
111 @title = "Viewing trace #{@trace.name}"
113 flash[:notice] = "Trace not found!"
114 redirect_to :controller => 'trace', :action => 'list'
116 rescue ActiveRecord::RecordNotFound
117 flash[:notice] = "Trace not found!"
118 redirect_to :controller => 'trace', :action => 'list'
123 logger.info(params[:trace][:gpx_file].class.name)
124 if params[:trace][:gpx_file].respond_to?(:read)
125 do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
126 params[:trace][:description], params[:trace][:public])
129 logger.info("id is #{@trace.id}")
130 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."
132 redirect_to :action => 'mine'
135 @trace = Trace.new({:name => "Dummy",
136 :tagstring => params[:trace][:tagstring],
137 :description => params[:trace][:description],
138 :public => params[:trace][:public],
139 :inserted => false, :user => @user,
140 :timestamp => Time.now.getutc})
142 @trace.errors.add(:gpx_file, "can't be blank")
145 @title = I18n.t('trace.create.upload_trace')
149 trace = Trace.find(params[:id])
151 if trace.visible? and (trace.public? or (@user and @user == trace.user))
152 if request.format == Mime::XML
153 send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment')
155 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
158 render :nothing => true, :status => :not_found
160 rescue ActiveRecord::RecordNotFound
161 render :nothing => true, :status => :not_found
165 @trace = Trace.find(params[:id])
167 if @user and @trace.user == @user
169 @trace.description = params[:trace][:description]
170 @trace.tagstring = params[:trace][:tagstring]
172 redirect_to :action => 'view'
176 render :nothing => true, :status => :forbidden
178 rescue ActiveRecord::RecordNotFound
179 render :nothing => true, :status => :not_found
183 trace = Trace.find(params[:id])
185 if @user and trace.user == @user
186 if request.post? and trace.visible?
187 trace.visible = false
189 flash[:notice] = 'Track scheduled for deletion'
190 redirect_to :controller => 'traces', :action => 'mine'
192 render :nothing => true, :status => :bad_request
195 render :nothing => true, :status => :forbidden
197 rescue ActiveRecord::RecordNotFound
198 render :nothing => true, :status => :not_found
202 trace = Trace.find(params[:id])
204 if @user and trace.user == @user
205 if request.post? and !trace.public?
208 flash[:notice] = 'Track made public'
209 redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
211 render :nothing => true, :status => :bad_request
214 render :nothing => true, :status => :forbidden
216 rescue ActiveRecord::RecordNotFound
217 render :nothing => true, :status => :not_found
221 conditions = ["gpx_files.public = ?", true]
223 if params[:display_name]
224 conditions[0] += " AND users.display_name = ?"
225 conditions << params[:display_name]
229 conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
230 conditions << params[:tag]
233 traces = Trace.find(:all, :include => :user, :conditions => conditions,
234 :order => "timestamp DESC", :limit => 20)
236 rss = OSM::GeoRSS.new
238 traces.each do |trace|
239 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)
242 render :text => rss.to_s, :content_type => "application/rss+xml"
246 trace = Trace.find(params[:id])
249 if trace.public? or (@user and @user == trace.user)
250 expires_in 7.days, :private => !trace.public, :public => trace.public
251 send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
253 render :nothing => true, :status => :forbidden
256 render :nothing => true, :status => :not_found
258 rescue ActiveRecord::RecordNotFound
259 render :nothing => true, :status => :not_found
263 trace = Trace.find(params[:id])
266 if trace.public? or (@user and @user == trace.user)
267 expires_in 7.days, :private => !trace.public, :public => trace.public
268 send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
270 render :nothing => true, :status => :forbidden
273 render :nothing => true, :status => :not_found
275 rescue ActiveRecord::RecordNotFound
276 render :nothing => true, :status => :not_found
280 trace = Trace.find(params[:id])
282 if trace.public? or trace.user == @user
283 render :text => trace.to_xml.to_s, :content_type => "text/xml"
285 render :nothing => true, :status => :forbidden
287 rescue ActiveRecord::RecordNotFound
288 render :nothing => true, :status => :not_found
292 trace = Trace.find(params[:id])
294 if trace.public? or trace.user == @user
295 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
297 render :nothing => true, :status => :forbidden
299 rescue ActiveRecord::RecordNotFound
300 render :nothing => true, :status => :not_found
305 tags = params[:tags] || ""
306 description = params[:description] || ""
307 pub = params[:public] || false
309 if params[:file].respond_to?(:read)
310 do_create(params[:file], tags, description, pub)
313 render :text => @trace.id.to_s, :content_type => "text/plain"
315 render :nothing => true, :status => :internal_server_error
317 render :nothing => true, :status => :bad_request
320 render :nothing => true, :status => :bad_request
323 render :nothing => true, :status => :method_not_allowed
329 def do_create(file, tags, description, public)
330 # Sanitise the user's filename
331 name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
333 # Get a temporary filename...
334 filename = "/tmp/#{rand}"
336 # ...and save the uploaded file to that location
337 File.open(filename, "w") { |f| f.write(file.read) }
339 # Create the trace object, falsely marked as already
340 # inserted to stop the import daemon trying to load it
344 :description => description,
348 :timestamp => Time.now.getutc
351 # Save the trace object
353 # Rename the temporary file to the final name
354 FileUtils.mv(filename, @trace.trace_name)
356 # Clear the inserted flag to make the import daemon load the trace
357 @trace.inserted = false
360 # Remove the file as we have failed to update the database
361 FileUtils.rm_f(filename)
364 # Finally save whether the user marked the trace as being public
366 if @user.trace_public_default.nil?
367 @user.preferences.create(:k => "gps.trace.public", :v => "default")
370 pref = @user.trace_public_default
371 pref.destroy unless pref.nil?