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