1 class TraceController < ApplicationController
4 before_filter :set_locale
5 before_filter :authorize_web
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")
148 trace = Trace.find(params[:id])
150 if trace.visible? and (trace.public? or (@user and @user == trace.user))
151 if request.format == Mime::XML
152 send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment')
154 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
157 render :nothing => true, :status => :not_found
159 rescue ActiveRecord::RecordNotFound
160 render :nothing => true, :status => :not_found
164 @trace = Trace.find(params[:id])
166 if @user and @trace.user == @user
168 @trace.description = params[:trace][:description]
169 @trace.tagstring = params[:trace][:tagstring]
171 redirect_to :action => 'view'
175 render :nothing => true, :status => :forbidden
177 rescue ActiveRecord::RecordNotFound
178 render :nothing => true, :status => :not_found
182 trace = Trace.find(params[:id])
184 if @user and trace.user == @user
185 if request.post? and trace.visible?
186 trace.visible = false
188 flash[:notice] = 'Track scheduled for deletion'
189 redirect_to :controller => 'traces', :action => 'mine'
191 render :nothing => true, :status => :bad_request
194 render :nothing => true, :status => :forbidden
196 rescue ActiveRecord::RecordNotFound
197 render :nothing => true, :status => :not_found
201 trace = Trace.find(params[:id])
203 if @user and trace.user == @user
204 if request.post? and !trace.public?
207 flash[:notice] = 'Track made public'
208 redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
210 render :nothing => true, :status => :bad_request
213 render :nothing => true, :status => :forbidden
215 rescue ActiveRecord::RecordNotFound
216 render :nothing => true, :status => :not_found
220 conditions = ["gpx_files.public = ?", true]
222 if params[:display_name]
223 conditions[0] += " AND users.display_name = ?"
224 conditions << params[:display_name]
228 conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
229 conditions << params[:tag]
232 traces = Trace.find(:all, :include => :user, :conditions => conditions,
233 :order => "timestamp DESC", :limit => 20)
235 rss = OSM::GeoRSS.new
237 traces.each do |trace|
238 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)
241 render :text => rss.to_s, :content_type => "application/rss+xml"
245 trace = Trace.find(params[:id])
248 if trace.public? or (@user and @user == trace.user)
249 expires_in 7.days, :private => !trace.public, :public => trace.public
250 send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
252 render :nothing => true, :status => :forbidden
255 render :nothing => true, :status => :not_found
257 rescue ActiveRecord::RecordNotFound
258 render :nothing => true, :status => :not_found
262 trace = Trace.find(params[:id])
265 if trace.public? or (@user and @user == trace.user)
266 expires_in 7.days, :private => !trace.public, :public => trace.public
267 send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
269 render :nothing => true, :status => :forbidden
272 render :nothing => true, :status => :not_found
274 rescue ActiveRecord::RecordNotFound
275 render :nothing => true, :status => :not_found
279 trace = Trace.find(params[:id])
281 if trace.public? or trace.user == @user
282 render :text => trace.to_xml.to_s, :content_type => "text/xml"
284 render :nothing => true, :status => :forbidden
286 rescue ActiveRecord::RecordNotFound
287 render :nothing => true, :status => :not_found
291 trace = Trace.find(params[:id])
293 if trace.public? or trace.user == @user
294 send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
296 render :nothing => true, :status => :forbidden
298 rescue ActiveRecord::RecordNotFound
299 render :nothing => true, :status => :not_found
304 tags = params[:tags] || ""
305 description = params[:description] || ""
306 pub = params[:public] || false
308 if params[:file].respond_to?(:read)
309 do_create(params[:file], tags, description, pub)
312 render :text => @trace.id.to_s, :content_type => "text/plain"
314 render :nothing => true, :status => :internal_server_error
316 render :nothing => true, :status => :bad_request
319 render :nothing => true, :status => :bad_request
322 render :nothing => true, :status => :method_not_allowed
328 def do_create(file, tags, description, public)
329 # Sanitise the user's filename
330 name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
332 # Get a temporary filename...
333 filename = "/tmp/#{rand}"
335 # ...and save the uploaded file to that location
336 File.open(filename, "w") { |f| f.write(file.read) }
338 # Create the trace object, falsely marked as already
339 # inserted to stop the import daemon trying to load it
343 :description => description,
347 :timestamp => Time.now.getutc
350 # Save the trace object
352 # Rename the temporary file to the final name
353 FileUtils.mv(filename, @trace.trace_name)
355 # Clear the inserted flag to make the import daemon load the trace
356 @trace.inserted = false
359 # Remove the file as we have failed to update the database
360 FileUtils.rm_f(filename)
363 # Finally save whether the user marked the trace as being public
365 if @user.trace_public_default.nil?
366 @user.preferences.create(:k => "gps.trace.public", :v => "default")
369 pref = @user.trace_public_default
370 pref.destroy unless pref.nil?