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