Use the rails paginator to paginate the trace list, and abstract some
[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   end
92
93   def create
94     name = params[:trace][:gpx_file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
95
96     do_create(name, params[:trace][:tagstring], params[:trace][:description], params[:trace][:public]) do |f|
97       f.write(params[:trace][:gpx_file].read)
98     end
99
100     if @trace.id
101       logger.info("id is #{@trace.id}")
102       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."
103
104       redirect_to :action => 'mine'
105     end
106   end
107
108   def data
109     trace = Trace.find(params[:id])
110     if trace and (trace.public? or (@user and @user == trace.user))
111       send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment')
112     else
113       render :nothing, :status => :not_found
114     end
115   end
116
117   def make_public
118     trace = Trace.find(params[:id])
119     if @user and trace.user == @user and !trace.public
120       trace.public = true
121       trace.save
122       flash[:notice] = 'Track made public'
123       redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
124     end
125   end
126
127   def georss
128     traces = Trace.find(:all, :conditions => ['public = true'], :order => 'timestamp DESC', :limit => 20)
129
130     rss = OSM::GeoRSS.new
131
132     #def add(latitude=0, longitude=0, title_text='dummy title', url='http://www.example.com/', description_text='dummy description', timestamp=Time.now)
133     traces.each do |trace|
134       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)
135     end
136
137     render :text => rss.to_s, :content_type => "application/rss+xml"
138   end
139
140   def picture
141     begin
142       trace = Trace.find(params[:id])
143
144       if trace.public? or (@user and @user == trace.user)
145         send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
146       else
147         render :nothing, :status => :forbidden
148       end
149     rescue ActiveRecord::RecordNotFound
150       render :nothing => true, :status => :not_found
151     rescue
152       render :nothing => true, :status => :internal_server_error
153     end
154   end
155
156   def icon
157     begin
158       trace = Trace.find(params[:id])
159
160       if trace.public? or (@user and @user == trace.user)
161         send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline')
162       else
163         render :nothing, :status => :forbidden
164       end
165     rescue ActiveRecord::RecordNotFound
166       render :nothing => true, :status => :not_found
167     rescue
168       render :nothing => true, :status => :internal_server_error
169     end
170   end
171
172   def api_details
173     begin
174       trace = Trace.find(params[:id])
175
176       if trace.public? or trace.user == @user
177         render :text => trace.to_xml.to_s, :content_type => "text/xml"
178       else
179         render :nothing => true, :status => :forbidden
180       end
181     rescue ActiveRecord::RecordNotFound
182       render :nothing => true, :status => :not_found
183     rescue
184       render :nothing => true, :status => :internal_server_error
185     end
186   end
187
188   def api_data
189     render :action => 'data'
190   end
191
192   def api_create
193     do_create(params[:filename], params[:tags], params[:description], true) do |f|
194       f.write(request.raw_post)
195     end
196
197     if @trace.id
198       render :nothing => true
199     else
200       render :nothing => true, :status => :internal_server_error
201     end
202   end
203
204 private
205
206   def do_create(name, tags, description, public)
207     filename = "/tmp/#{rand}"
208
209     File.open(filename, "w") { |f| yield f }
210
211     @trace = Trace.new({:name => name, :tagstring => tags,
212                         :description => description, :public => public})
213     @trace.inserted = false
214     @trace.user = @user
215     @trace.timestamp = Time.now
216
217     if @trace.save
218       File.rename(filename, @trace.trace_name)
219     else
220       FileUtils.rm_f(filename)
221     end
222   end
223
224 end