Return "404 Not Found" for users and traces which don't exist.
[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   #  paging_action - the action that will be linked back to from view
9   def list (target_user = nil, paging_action = 'list')
10     @title = 'public GPS traces'
11     @title += " tagged with #{params[:tag]}" if params[:tag]
12     page_index = params[:page] ? params[:page].to_i - 1 : 0 # nice 1-based page -> 0-based page index
13
14     # from display name, pick up user id if one user's traces only
15     display_name = params[:display_name]
16     if target_user.nil? and display_name and display_name != ''
17       @paging_action = 'view'
18       @display_name = display_name
19       @title += " from #{@display_name}"
20       target_user = User.find(:first, :conditions => [ "display_name = ?", display_name])
21     end
22
23     opt = Hash.new
24     opt[:include] = [:user, :tags] # load users and tags from db at same time as traces
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 = ["(public = 1 OR user_id = ?)", @user.id] #1
34       else
35         conditions  = ["public = 1"] #2
36       end
37     else
38       if @user and @user.id == target_user.id
39         conditions = ["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 = ["public = 1 AND user_id = ?", target_user.id] #4
42       end
43     end
44     conditions[0] += " AND users.display_name != ''" # users need to set display name before traces will be exposed
45     
46     opt[:order] = 'timestamp DESC'
47     if params[:tag]
48       @tag = params[:tag]
49       conditions[0] += " AND gpx_file_tags.tag = ?"
50       conditions << @tag;
51     end
52     
53     opt[:conditions] = conditions
54     opt[:per_page] = 20
55
56     @trace_pages, @traces = paginate(:traces, opt)
57     
58     # put together SET of tags across traces, for related links
59     tagset = Hash.new
60     if @traces
61       @traces.each do |trace|
62         trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
63         trace.tags.each do |tag|
64           tagset[tag.tag] = tag.tag
65         end
66       end
67     end
68     
69     # final helper vars for view
70     @display_name = display_name
71     @all_tags = tagset.values
72 ##    @paging_action = paging_action # the action that paging requests should route back to, e.g. 'list' or 'mine'
73 ##    @page = page_index + 1 # nice 1-based external page numbers
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     unless @trace.public
87       if @user
88         render :nothing, :status => :forbidden if @trace.user.id != @user.id
89       end
90     end
91   rescue ActiveRecord::RecordNotFound
92     render :nothing => true, :status => :not_found
93   end
94
95   def create
96     name = params[:trace][:gpx_file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
97
98     do_create(name, params[:trace][:tagstring], params[:trace][:description], params[:trace][:public]) do |f|
99       f.write(params[:trace][:gpx_file].read)
100     end
101
102     if @trace.id
103       logger.info("id is #{@trace.id}")
104       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."
105
106       redirect_to :action => 'mine'
107     end
108   end
109
110   def data
111     trace = Trace.find(params[:id])
112     if trace and (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   end
118
119   def make_public
120     trace = Trace.find(params[:id])
121     if @user and trace.user == @user and !trace.public
122       trace.public = true
123       trace.save
124       flash[:notice] = 'Track made public'
125       redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
126     end
127   end
128
129   def georss
130     traces = Trace.find(:all, :conditions => ['public = true'], :order => 'timestamp DESC', :limit => 20)
131
132     rss = OSM::GeoRSS.new
133
134     #def add(latitude=0, longitude=0, title_text='dummy title', url='http://www.example.com/', description_text='dummy description', timestamp=Time.now)
135     traces.each do |trace|
136       rss.add(trace.latitude, trace.longitude, trace.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)
137     end
138
139     render :text => rss.to_s, :content_type => "application/rss+xml"
140   end
141
142   def picture
143     begin
144       trace = Trace.find(params[:id])
145
146       if trace.public? or (@user and @user == trace.user)
147         send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
148       else
149         render :nothing, :status => :forbidden
150       end
151     rescue ActiveRecord::RecordNotFound
152       render :nothing => true, :status => :not_found
153     rescue
154       render :nothing => true, :status => :internal_server_error
155     end
156   end
157
158   def icon
159     begin
160       trace = Trace.find(params[:id])
161
162       if trace.public? or (@user and @user == trace.user)
163         send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
164       else
165         render :nothing, :status => :forbidden
166       end
167     rescue ActiveRecord::RecordNotFound
168       render :nothing => true, :status => :not_found
169     rescue
170       render :nothing => true, :status => :internal_server_error
171     end
172   end
173
174   def api_details
175     begin
176       trace = Trace.find(params[:id])
177
178       if trace.public? or trace.user == @user
179         render :text => trace.to_xml.to_s, :content_type => "text/xml"
180       else
181         render :nothing => true, :status => :forbidden
182       end
183     rescue ActiveRecord::RecordNotFound
184       render :nothing => true, :status => :not_found
185     rescue
186       render :nothing => true, :status => :internal_server_error
187     end
188   end
189
190   def api_data
191     render :action => 'data'
192   end
193
194   def api_create
195     do_create(params[:filename], params[:tags], params[:description], true) do |f|
196       f.write(request.raw_post)
197     end
198
199     if @trace.id
200       render :nothing => true
201     else
202       render :nothing => true, :status => :internal_server_error
203     end
204   end
205
206 private
207
208   def do_create(name, tags, description, public)
209     filename = "/tmp/#{rand}"
210
211     File.open(filename, "w") { |f| yield f }
212
213     @trace = Trace.new({:name => name, :tagstring => tags,
214                         :description => description, :public => public})
215     @trace.inserted = false
216     @trace.user = @user
217     @trace.timestamp = Time.now
218
219     if @trace.save
220       File.rename(filename, @trace.trace_name)
221     else
222       FileUtils.rm_f(filename)
223     end
224   end
225
226 end