Put the option to delete GPS traces back. Fixes #405.
[rails.git] / app / controllers / trace_controller.rb
1 class TraceController < ApplicationController
2   before_filter :authorize_web  
3   before_filter :authorize, :only => [:api_details, :api_data, :api_create]
4   layout 'site'
5  
6   # Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
7   #  target_user - if set, specifies the user to fetch traces for.  if not set will fetch all traces
8   def list(target_user = nil, action = "list")
9     # from display name, pick up user id if one user's traces only
10     display_name = params[:display_name]
11     if target_user.nil? and !display_name.blank?
12       target_user = User.find(:first, :conditions => [ "display_name = ?", display_name])
13     end
14
15     # set title
16     if target_user.nil?
17       @title = "Public GPS traces"
18     elsif @user and @user.id == target_user.id
19       @title = "Your GPS traces"
20     else
21       @title = "Public GPS traces from #{target_user.display_name}"
22     end
23
24     @title += " tagged with #{params[:tag]}" if params[:tag]
25
26     # four main cases:
27     # 1 - all traces, logged in = all public traces + all user's (i.e + all mine)
28     # 2 - all traces, not logged in = all public traces
29     # 3 - user's traces, logged in as same user = all user's traces 
30     # 4 - user's traces, not logged in as that user = all user's public traces
31     if target_user.nil? # all traces
32       if @user
33         conditions = ["(gpx_files.public = 1 OR gpx_files.user_id = ?)", @user.id] #1
34       else
35         conditions  = ["gpx_files.public = 1"] #2
36       end
37     else
38       if @user and @user.id == target_user.id
39         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)
40       else
41         conditions = ["gpx_files.public = 1 AND gpx_files.user_id = ?", target_user.id] #4
42       end
43     end
44     
45     if params[:tag]
46       @tag = params[:tag]
47       conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
48       conditions << @tag
49     end
50     
51     conditions[0] += " AND gpx_files.visible = 1"
52
53     @trace_pages, @traces = paginate(:traces,
54                                      :include => [:user, :tags],
55                                      :conditions => conditions,
56                                      :order => "gpx_files.timestamp DESC",
57                                      :per_page => 20)
58
59     # put together SET of tags across traces, for related links
60     tagset = Hash.new
61     if @traces
62       @traces.each do |trace|
63         trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
64         trace.tags.each do |tag|
65           tagset[tag.tag] = tag.tag
66         end
67       end
68     end
69     
70     # final helper vars for view
71     @action = action
72     @display_name = target_user.display_name if target_user
73     @all_tags = tagset.values
74   end
75
76   def mine
77     if @user
78       list(@user, "mine") unless @user.nil?
79     else
80       redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri
81     end
82   end
83
84   def view
85     @trace = Trace.find(params[:id])
86     @title = "Viewing trace #{@trace.name}"
87     if !@trace.visible?
88       render :nothing => true, :status => :not_found
89     elsif !@trace.public? and @trace.user.id != @user.id
90       render :nothing => true, :status => :forbidden
91     end
92   rescue ActiveRecord::RecordNotFound
93     render :nothing => true, :status => :not_found
94   end
95
96   def create
97     name = params[:trace][:gpx_file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
98
99     do_create(name, params[:trace][:tagstring], params[:trace][:description], params[:trace][:public]) do |f|
100       f.write(params[:trace][:gpx_file].read)
101     end
102
103     if @trace.id
104       logger.info("id is #{@trace.id}")
105       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."
106
107       redirect_to :action => 'mine'
108     end
109   end
110
111   def data
112     trace = Trace.find(params[:id])
113
114     if trace.visible? and (trace.public? or (@user and @user == trace.user))
115       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
116     else
117       render :nothing, :status => :not_found
118     end
119   rescue ActiveRecord::RecordNotFound
120     render :nothing => true, :status => :not_found
121   end
122
123   def delete
124     trace = Trace.find(params[:id])
125
126     if @user and trace.user == @user
127       if request.post? and trace.visible?
128         trace.visible = false
129         trace.save
130         flash[:notice] = 'Track scheduled for deletion'
131         redirect_to :controller => 'traces', :action => 'mine'
132       else
133         render :nothing, :status => :bad_request
134       end
135     else
136       render :nothing, :status => :forbidden
137     end
138   rescue ActiveRecord::RecordNotFound
139     render :nothing => true, :status => :not_found
140   end
141
142   def make_public
143     trace = Trace.find(params[:id])
144
145     if @user and trace.user == @user
146       if request.post? and !trace.public?
147         trace.public = true
148         trace.save
149         flash[:notice] = 'Track made public'
150         redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
151       else
152         render :nothing, :status => :bad_request
153       end
154     else
155       render :nothing, :status => :forbidden
156     end
157   rescue ActiveRecord::RecordNotFound
158     render :nothing => true, :status => :not_found
159   end
160
161   def georss
162     conditions = ["gpx_files.public = 1"]
163
164     if params[:display_name]
165       conditions[0] += " AND users.display_name = ?"
166       conditions << params[:display_name]
167     end
168     
169     if params[:tag]
170       conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
171       conditions << params[:tag]
172     end
173
174     traces = Trace.find(:all, :include => :user, :conditions => conditions, 
175                         :order => "timestamp DESC", :limit => 20)
176
177     rss = OSM::GeoRSS.new
178
179     traces.each do |trace|
180       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)
181     end
182
183     render :text => rss.to_s, :content_type => "application/rss+xml"
184   end
185
186   def picture
187     trace = Trace.find(params[:id])
188
189     if trace.inserted?
190       if trace.public? or (@user and @user == trace.user)
191         send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
192       else
193         render :nothing, :status => :forbidden
194       end
195     else
196       render :nothing => true, :status => :not_found
197     end
198   rescue ActiveRecord::RecordNotFound
199     render :nothing => true, :status => :not_found
200   end
201
202   def icon
203     trace = Trace.find(params[:id])
204
205     if trace.inserted?
206       if trace.public? or (@user and @user == trace.user)
207         send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
208       else
209         render :nothing, :status => :forbidden
210       end
211     else
212       render :nothing => true, :status => :not_found
213     end
214   rescue ActiveRecord::RecordNotFound
215     render :nothing => true, :status => :not_found
216   end
217
218   def api_details
219     trace = Trace.find(params[:id])
220
221     if trace.public? or trace.user == @user
222       render :text => trace.to_xml.to_s, :content_type => "text/xml"
223     else
224       render :nothing => true, :status => :forbidden
225     end
226   rescue ActiveRecord::RecordNotFound
227     render :nothing => true, :status => :not_found
228   end
229
230   def api_data
231     trace = Trace.find(params[:id])
232
233     if trace.public? or trace.user == @user
234       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
235     else
236       render :nothing => true, :status => :forbidden
237     end
238   rescue ActiveRecord::RecordNotFound
239     render :nothing => true, :status => :not_found
240   end
241
242   def api_create
243     if request.post?
244       name = params[:file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
245
246       do_create(name, params[:tags], params[:description], params[:public]) do |f|
247         f.write(params[:file].read)
248       end
249
250       if @trace.id
251         render :text => @trace.id.to_s, :content_type => "text/plain"
252       elsif @trace.valid?
253         render :nothing => true, :status => :internal_server_error
254       else
255         render :nothing => true, :status => :bad_request
256       end
257     else
258       render :nothing => true, :status => :method_not_allowed
259     end
260   end
261
262 private
263
264   def do_create(name, tags, description, public)
265     filename = "/tmp/#{rand}"
266
267     File.open(filename, "w") { |f| yield f }
268
269     @trace = Trace.new({:name => name, :tagstring => tags,
270                         :description => description, :public => public})
271     @trace.inserted = false
272     @trace.user = @user
273     @trace.timestamp = Time.now
274
275     if @trace.save
276       File.rename(filename, @trace.trace_name)
277     else
278       FileUtils.rm_f(filename)
279     end
280   end
281
282 end