Make next and previous links on user/XXX/traces work. Fixes #501.
[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     @traces_per_page = 20
11     page_index = params[:page] ? params[:page].to_i - 1 : 0 # nice 1-based page -> 0-based page index
12
13     # from display name, pick up user id if one user's traces only
14     display_name = params[:display_name]
15     if target_user.nil? and display_name and display_name != ''
16       @paging_action = 'view'
17       @display_name = display_name
18       target_user = User.find(:first, :conditions => [ "display_name = ?", display_name])
19     end
20
21     opt = Hash.new
22     opt[:include] = [:user, :tags] # load users and tags from db at same time as traces
23
24     # four main cases:
25     # 1 - all traces, logged in = all public traces + all user's (i.e + all mine)
26     # 2 - all traces, not logged in = all public traces
27     # 3 - user's traces, logged in as same user = all user's traces 
28     # 4 - user's traces, not logged in as that user = all user's public traces
29     if target_user.nil? # all traces
30       if @user
31         conditions = ["(public = 1 OR user_id = ?)", @user.id] #1
32       else
33         conditions  = ["public = 1"] #2
34       end
35     else
36       if @user and @user.id == target_user.id
37         conditions = ["user_id = ?", @user.id] #3 (check vs user id, so no join + can't pick up non-public traces by changing name)
38       else
39         conditions = ["public = 1 AND user_id = ?", target_user.id] #4
40       end
41     end
42     conditions[0] += " AND users.display_name != ''" # users need to set display name before traces will be exposed
43     
44     opt[:order] = 'timestamp DESC'
45     if params[:tag]
46       @tag = params[:tag]
47       conditions[0] += " AND gpx_file_tags.tag = ?"
48       conditions << @tag;
49     end
50     
51     opt[:conditions] = conditions
52
53     # count traces using all options except limit
54     @max_trace = Trace.count(opt)
55     @max_page = Integer((@max_trace + 1) / @traces_per_page) 
56     
57     # last step before fetch - add paging options
58     opt[:limit] = @traces_per_page
59     if page_index > 0
60       opt[:offset] = @traces_per_page * page_index
61     end
62
63     @traces = Trace.find(:all , opt)
64     
65     # put together SET of tags across traces, for related links
66     tagset = Hash.new
67     if @traces
68       @traces.each do |trace|
69         trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
70         trace.tags.each do |tag|
71           tagset[tag.tag] = tag.tag
72         end
73       end
74     end
75     
76     # final helper vars for view
77     @display_name = display_name
78     @all_tags = tagset.values
79     @paging_action = paging_action # the action that paging requests should route back to, e.g. 'list' or 'mine'
80     @page = page_index + 1 # nice 1-based external page numbers
81   end
82
83   def mine
84     if @user
85       list(@user, 'mine') unless @user.nil?
86     else
87       redirect_to :controller => 'user', :action => 'login'
88     end
89   end
90
91   def view
92     @trace = Trace.find(params[:id])
93     unless @trace.public
94       if @user
95         render :nothing, :status => 401 if @trace.user.id != @user.id
96       end
97     end
98   end
99
100   def create
101     filename = "/tmp/#{rand}"
102
103     File.open(filename, "w") { |f| f.write(params[:trace][:gpx_file].read) }
104     params[:trace][:name] = params[:trace][:gpx_file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
105     params[:trace].delete('gpx_file') # remove the field from the hash, because there's no such field in the DB
106     @trace = Trace.new(params[:trace])
107     @trace.inserted = false
108     @trace.user = @user
109     @trace.timestamp = Time.now
110
111     if @trace.save
112       saved_filename = "/home/osm/gpx/#{@trace.id}.gpx"
113       File.rename(filename, saved_filename)
114
115       logger.info("id is #{@trace.id}")
116       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."
117       redirect_to :action => 'mine'
118     else
119       # fixme throw an error here
120        redirect_to :action => 'mine'
121        flash[:notice] = "You haven't entered a tag or a description for yoru traces."
122     end
123   end
124
125   def data
126     trace = Trace.find(params[:id])
127     if trace.public? or (@user and @user == trace.user)
128       send_data(File.open("/home/osm/gpx/#{trace.id}.gpx",'r').read , :filename => "#{trace.id}.gpx", :type => 'text/plain', :disposition => 'inline')
129     end
130   end
131
132   def make_public
133     trace = Trace.find(params[:id])
134     if @user and trace.user == @user and !trace.public
135       trace.public = true
136       trace.save
137       flash[:notice] = 'Track made public'
138       redirect_to :controller => 'trace', :action => 'view', :id => params[:id]
139     end
140   end
141
142   def georss
143     traces = Trace.find(:all, :conditions => ['public = true'], :order => 'timestamp DESC', :limit => 20)
144
145     rss = OSM::GeoRSS.new
146
147     #def add(latitude=0, longitude=0, title_text='dummy title', url='http://www.example.com/', description_text='dummy description', timestamp=Time.now)
148     traces.each do |trace|
149       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)
150     end
151
152     response.headers["Content-Type"] = 'application/xml+rss'
153
154     render :text => rss.to_s
155   end
156
157   def picture
158     trace = Trace.find(params[:id])
159     send_data(trace.large_picture, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline') if trace.public? or (@user and @user == trace.user)
160   end
161
162   def icon
163     trace = Trace.find(params[:id])
164     send_data(trace.icon_picture, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline') if trace.public? or (@user and @user == trace.user)
165   end
166
167   def api_details
168     trace = Trace.find(params[:id])
169     doc = OSM::API.new.get_xml_doc
170     doc.root << trace.to_xml_node() if trace.public? or trace.user == @user
171     render :text => doc.to_s
172   end
173
174   def api_data
175     render :action => 'data'
176   end
177
178   def api_create
179     #FIXME merge this code with create as they're pretty similar?
180     
181     filename = "/tmp/#{rand}"
182     File.open(filename, "w") { |f| f.write(request.raw_post) }
183     params[:trace] = {}
184     params[:trace][:name] = params[:filename]
185     params[:trace][:tagstring] = params[:tags]
186     params[:trace][:description] = params[:description]
187     @trace = Trace.new(params[:trace])
188     @trace.inserted = false
189     @trace.user = @user
190     @trace.timestamp = Time.now
191
192     if @trace.save
193       saved_filename = "/home/osm/gpx/#{@trace.id}.gpx"
194       File.rename(filename, saved_filename)
195       logger.info("id is #{@trace.id}")
196       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."
197       render :nothing => true
198     else
199       render :nothing => true, :status => 400 # er FIXME what fricking code to return?
200     end
201
202   end
203 end