Handle errors a bit better.
[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     @trace_pages, @traces = paginate(:traces,
52                                      :include => [:user, :tags],
53                                      :conditions => conditions,
54                                      :order => "gpx_files.timestamp DESC",
55                                      :per_page => 20)
56
57     # put together SET of tags across traces, for related links
58     tagset = Hash.new
59     if @traces
60       @traces.each do |trace|
61         trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
62         trace.tags.each do |tag|
63           tagset[tag.tag] = tag.tag
64         end
65       end
66     end
67     
68     # final helper vars for view
69     @action = action
70     @display_name = target_user.display_name if target_user
71     @all_tags = tagset.values
72   end
73
74   def mine
75     if @user
76       list(@user, "mine") unless @user.nil?
77     else
78       redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri
79     end
80   end
81
82   def view
83     @trace = Trace.find(params[:id])
84     @title = "Viewing trace #{@trace.name}"
85     unless @trace.public
86       if @user
87         render :nothing, :status => :forbidden if @trace.user.id != @user.id
88       end
89     end
90   rescue ActiveRecord::RecordNotFound
91     render :nothing => true, :status => :not_found
92   end
93
94   def create
95     name = params[:trace][:gpx_file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
96
97     do_create(name, params[:trace][:tagstring], params[:trace][:description], params[:trace][:public]) do |f|
98       f.write(params[:trace][:gpx_file].read)
99     end
100
101     if @trace.id
102       logger.info("id is #{@trace.id}")
103       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."
104
105       redirect_to :action => 'mine'
106     end
107   end
108
109   def data
110     trace = Trace.find(params[:id])
111
112     if trace.public? or (@user and @user == trace.user)
113       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
114     else
115       render :nothing, :status => :not_found
116     end
117   rescue ActiveRecord::RecordNotFound
118     render :nothing => true, :status => :not_found
119   end
120
121   def make_public
122     trace = Trace.find(params[:id])
123     if @user and trace.user == @user and !trace.public
124       trace.public = true
125       trace.save
126       flash[:notice] = 'Track made public'
127       redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
128     end
129   end
130
131   def georss
132     conditions = ["gpx_files.public = 1"]
133
134     if params[:display_name]
135       conditions[0] += " AND users.display_name = ?"
136       conditions << params[:display_name]
137     end
138     
139     if params[:tag]
140       conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
141       conditions << params[:tag]
142     end
143
144     traces = Trace.find(:all, :include => :user, :conditions => conditions, 
145                         :order => "timestamp DESC", :limit => 20)
146
147     rss = OSM::GeoRSS.new
148
149     traces.each do |trace|
150       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)
151     end
152
153     render :text => rss.to_s, :content_type => "application/rss+xml"
154   end
155
156   def picture
157     trace = Trace.find(params[:id])
158
159     if trace.inserted?
160       if trace.public? or (@user and @user == trace.user)
161         send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
162       else
163         render :nothing, :status => :forbidden
164       end
165     else
166       render :nothing => true, :status => :not_found
167     end
168   rescue ActiveRecord::RecordNotFound
169     render :nothing => true, :status => :not_found
170   end
171
172   def icon
173     trace = Trace.find(params[:id])
174
175     if trace.inserted?
176       if trace.public? or (@user and @user == trace.user)
177         send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
178       else
179         render :nothing, :status => :forbidden
180       end
181     else
182       render :nothing => true, :status => :not_found
183     end
184   rescue ActiveRecord::RecordNotFound
185     render :nothing => true, :status => :not_found
186   end
187
188   def api_details
189     trace = Trace.find(params[:id])
190
191     if trace.public? or trace.user == @user
192       render :text => trace.to_xml.to_s, :content_type => "text/xml"
193     else
194       render :nothing => true, :status => :forbidden
195     end
196   rescue ActiveRecord::RecordNotFound
197     render :nothing => true, :status => :not_found
198   end
199
200   def api_data
201     trace = Trace.find(params[:id])
202
203     if trace.public? or trace.user == @user
204       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
205     else
206       render :nothing => true, :status => :forbidden
207     end
208   rescue ActiveRecord::RecordNotFound
209     render :nothing => true, :status => :not_found
210   end
211
212   def api_create
213     if request.post?
214       name = params[:file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
215
216       do_create(name, params[:tags], params[:description], params[:public]) do |f|
217         f.write(params[:file].read)
218       end
219
220       if @trace.id
221         render :text => @trace.id.to_s, :content_type => "text/plain"
222       elsif @trace.valid?
223         render :nothing => true, :status => :internal_server_error
224       else
225         render :nothing => true, :status => :bad_request
226       end
227     else
228       render :nothing => true, :status => :method_not_allowed
229     end
230   end
231
232 private
233
234   def do_create(name, tags, description, public)
235     filename = "/tmp/#{rand}"
236
237     File.open(filename, "w") { |f| yield f }
238
239     @trace = Trace.new({:name => name, :tagstring => tags,
240                         :description => description, :public => public})
241     @trace.inserted = false
242     @trace.user = @user
243     @trace.timestamp = Time.now
244
245     if @trace.save
246       File.rename(filename, @trace.trace_name)
247     else
248       FileUtils.rm_f(filename)
249     end
250   end
251
252 end