Resyncing from head 10895:11795
[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 = ? and display_name = ?", true, 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 = ? OR gpx_files.user_id = ?)", true, @user.id] #1
37       else
38         conditions  = ["gpx_files.public = ?", true] #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 = ? AND gpx_files.user_id = ?", true, 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 = ?"
55     conditions << true
56
57     @trace_pages, @traces = paginate(:traces,
58                                      :include => [:user, :tags],
59                                      :conditions => conditions,
60                                      :order => "gpx_files.timestamp DESC",
61                                      :per_page => 20)
62
63     # put together SET of tags across traces, for related links
64     tagset = Hash.new
65     if @traces
66       @traces.each do |trace|
67         trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
68         trace.tags.each do |tag|
69           tagset[tag.tag] = tag.tag
70         end
71       end
72     end
73     
74     # final helper vars for view
75     @action = action
76     @display_name = target_user.display_name if target_user
77     @all_tags = tagset.values
78   end
79
80   def mine
81     if @user
82       list(@user, "mine") unless @user.nil?
83     else
84       redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri
85     end
86   end
87
88   def view
89     @trace = Trace.find(params[:id])
90
91     if @trace and @trace.visible? and
92        (@trace.public? or @trace.user == @user)
93       @title = "Viewing trace #{@trace.name}"
94     else
95       flash[:notice] = "Trace not found!"
96       redirect_to :controller => 'trace', :action => 'list'
97     end
98   rescue ActiveRecord::RecordNotFound
99     flash[:notice] = "Trace not found!"
100     redirect_to :controller => 'trace', :action => 'list'
101   end
102
103   def create
104     logger.info(params[:trace][:gpx_file].class.name)
105     if params[:trace][:gpx_file].respond_to?(:read)
106       do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
107                 params[:trace][:description], params[:trace][:public])
108
109       if @trace.id
110         logger.info("id is #{@trace.id}")
111         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."
112
113         redirect_to :action => 'mine'
114       end
115     else
116       @trace = Trace.new({:name => "Dummy",
117                           :tagstring => params[:trace][:tagstring],
118                           :description => params[:trace][:description],
119                           :public => params[:trace][:public],
120                           :inserted => false, :user => @user,
121                           :timestamp => Time.now})
122       @trace.valid?
123       @trace.errors.add(:gpx_file, "can't be blank")
124     end
125   end
126
127   def data
128     trace = Trace.find(params[:id])
129
130     if trace.visible? and (trace.public? or (@user and @user == trace.user))
131       if request.format == Mime::XML
132         send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment')
133       else
134         send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
135       end
136     else
137       render :nothing, :status => :not_found
138     end
139   rescue ActiveRecord::RecordNotFound
140     render :nothing => true, :status => :not_found
141   end
142
143   def edit
144     @trace = Trace.find(params[:id])
145
146     if @user and @trace.user == @user
147       if params[:trace]
148         @trace.description = params[:trace][:description]
149         @trace.tagstring = params[:trace][:tagstring]
150         if @trace.save
151           redirect_to :action => 'view'
152         end        
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 delete
162     trace = Trace.find(params[:id])
163
164     if @user and trace.user == @user
165       if request.post? and trace.visible?
166         trace.visible = false
167         trace.save
168         flash[:notice] = 'Track scheduled for deletion'
169         redirect_to :controller => 'traces', :action => 'mine'
170       else
171         render :nothing, :status => :bad_request
172       end
173     else
174       render :nothing, :status => :forbidden
175     end
176   rescue ActiveRecord::RecordNotFound
177     render :nothing => true, :status => :not_found
178   end
179
180   def make_public
181     trace = Trace.find(params[:id])
182
183     if @user and trace.user == @user
184       if request.post? and !trace.public?
185         trace.public = true
186         trace.save
187         flash[:notice] = 'Track made public'
188         redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
189       else
190         render :nothing, :status => :bad_request
191       end
192     else
193       render :nothing, :status => :forbidden
194     end
195   rescue ActiveRecord::RecordNotFound
196     render :nothing => true, :status => :not_found
197   end
198
199   def georss
200     conditions = ["gpx_files.public = ?", true]
201
202     if params[:display_name]
203       conditions[0] += " AND users.display_name = ?"
204       conditions << params[:display_name]
205     end
206
207     if params[:tag]
208       conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
209       conditions << params[:tag]
210     end
211
212     traces = Trace.find(:all, :include => :user, :conditions => conditions, 
213                         :order => "timestamp DESC", :limit => 20)
214
215     rss = OSM::GeoRSS.new
216
217     traces.each do |trace|
218       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)
219     end
220
221     render :text => rss.to_s, :content_type => "application/rss+xml"
222   end
223
224   def picture
225     trace = Trace.find(params[:id])
226
227     if trace.inserted?
228       if trace.public? or (@user and @user == trace.user)
229         send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
230       else
231         render :nothing, :status => :forbidden
232       end
233     else
234       render :nothing => true, :status => :not_found
235     end
236   rescue ActiveRecord::RecordNotFound
237     render :nothing => true, :status => :not_found
238   end
239
240   def icon
241     trace = Trace.find(params[:id])
242
243     if trace.inserted?
244       if trace.public? or (@user and @user == trace.user)
245         send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
246       else
247         render :nothing, :status => :forbidden
248       end
249     else
250       render :nothing => true, :status => :not_found
251     end
252   rescue ActiveRecord::RecordNotFound
253     render :nothing => true, :status => :not_found
254   end
255
256   def api_details
257     trace = Trace.find(params[:id])
258
259     if trace.public? or trace.user == @user
260       render :text => trace.to_xml.to_s, :content_type => "text/xml"
261     else
262       render :nothing => true, :status => :forbidden
263     end
264   rescue ActiveRecord::RecordNotFound
265     render :nothing => true, :status => :not_found
266   end
267
268   def api_data
269     trace = Trace.find(params[:id])
270
271     if trace.public? or trace.user == @user
272       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
273     else
274       render :nothing => true, :status => :forbidden
275     end
276   rescue ActiveRecord::RecordNotFound
277     render :nothing => true, :status => :not_found
278   end
279
280   def api_create
281     if request.post?
282       do_create(params[:file], params[:tags], params[:description], params[:public])
283
284       if @trace.id
285         render :text => @trace.id.to_s, :content_type => "text/plain"
286       elsif @trace.valid?
287         render :nothing => true, :status => :internal_server_error
288       else
289         render :nothing => true, :status => :bad_request
290       end
291     else
292       render :nothing => true, :status => :method_not_allowed
293     end
294   end
295
296 private
297
298   def do_create(file, tags, description, public)
299     name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
300     filename = "/tmp/#{rand}"
301
302     File.open(filename, "w") { |f| f.write(file.read) }
303
304     @trace = Trace.new({:name => name, :tagstring => tags,
305                         :description => description, :public => public})
306     @trace.inserted = false
307     @trace.user = @user
308     @trace.timestamp = Time.now
309
310     if @trace.save
311       FileUtils.mv(filename, @trace.trace_name)
312     else
313       FileUtils.rm_f(filename)
314     end
315   end
316
317 end