Make the gpx/id/data API call work, and make gpx/create a POST method
[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     if trace and (trace.public? or (@user and @user == trace.user))
112       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
113     else
114       render :nothing, :status => :not_found
115     end
116   end
117
118   def make_public
119     trace = Trace.find(params[:id])
120     if @user and trace.user == @user and !trace.public
121       trace.public = true
122       trace.save
123       flash[:notice] = 'Track made public'
124       redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
125     end
126   end
127
128   def georss
129     conditions = ["gpx_files.public = 1"]
130
131     if params[:display_name]
132       conditions[0] += " AND users.display_name = ?"
133       conditions << params[:display_name]
134     end
135     
136     if params[:tag]
137       conditions[0] += " AND EXISTS (SELECT * FROM gpx_file_tags AS gft WHERE gft.gpx_id = gpx_files.id AND gft.tag = ?)"
138       conditions << params[:tag]
139     end
140
141     traces = Trace.find(:all, :include => :user, :conditions => conditions, 
142                         :order => "timestamp DESC", :limit => 20)
143
144     rss = OSM::GeoRSS.new
145
146     traces.each do |trace|
147       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)
148     end
149
150     render :text => rss.to_s, :content_type => "application/rss+xml"
151   end
152
153   def picture
154     trace = Trace.find(params[:id])
155
156     if trace.public? or (@user and @user == trace.user)
157       send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
158     else
159       render :nothing, :status => :forbidden
160     end
161   rescue ActiveRecord::RecordNotFound
162     render :nothing => true, :status => :not_found
163   end
164
165   def icon
166     trace = Trace.find(params[:id])
167
168     if trace.public? or (@user and @user == trace.user)
169       send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
170     else
171       render :nothing, :status => :forbidden
172     end
173   rescue ActiveRecord::RecordNotFound
174     render :nothing => true, :status => :not_found
175   end
176
177   def api_details
178     trace = Trace.find(params[:id])
179
180     if trace.public? or trace.user == @user
181       render :text => trace.to_xml.to_s, :content_type => "text/xml"
182     else
183       render :nothing => true, :status => :forbidden
184     end
185   rescue ActiveRecord::RecordNotFound
186     render :nothing => true, :status => :not_found
187   end
188
189   def api_data
190     trace = Trace.find(params[:id])
191
192     if trace.public? or trace.user == @user
193       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
194     else
195       render :nothing => true, :status => :forbidden
196     end
197   rescue ActiveRecord::RecordNotFound
198     render :nothing => true, :status => :not_found
199   end
200
201   def api_create
202     if request.post?
203       name = params[:file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
204
205       do_create(name, params[:tags], params[:description], params[:public]) do |f|
206         f.write(request[:file].read)
207       end
208
209       if @trace.id
210         render :text => @trace.id.to_s, :content_type => "text/plain"
211       elsif @trace.valid?
212         render :nothing => true, :status => :internal_server_error
213       else
214         render :nothing => true, :status => :bad_request
215       end
216     else
217       render :nothing => true, :status => :method_not_allowed
218     end
219   end
220
221 private
222
223   def do_create(name, tags, description, public)
224     filename = "/tmp/#{rand}"
225
226     File.open(filename, "w") { |f| yield f }
227
228     @trace = Trace.new({:name => name, :tagstring => tags,
229                         :description => description, :public => public})
230     @trace.inserted = false
231     @trace.user = @user
232     @trace.timestamp = Time.now
233
234     if @trace.save
235       File.rename(filename, @trace.trace_name)
236     else
237       FileUtils.rm_f(filename)
238     end
239   end
240
241 end