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