Adding titles to trace lists
[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     @traces_per_page = 20
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
55     # count traces using all options except limit
56     @max_trace = Trace.count(opt)
57     @max_page = Integer((@max_trace + 1) / @traces_per_page) 
58     
59     # last step before fetch - add paging options
60     opt[:limit] = @traces_per_page
61     if page_index > 0
62       opt[:offset] = @traces_per_page * page_index
63     end
64
65     @traces = Trace.find(:all , opt)
66     
67     # put together SET of tags across traces, for related links
68     tagset = Hash.new
69     if @traces
70       @traces.each do |trace|
71         trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
72         trace.tags.each do |tag|
73           tagset[tag.tag] = tag.tag
74         end
75       end
76     end
77     
78     # final helper vars for view
79     @display_name = display_name
80     @all_tags = tagset.values
81     @paging_action = paging_action # the action that paging requests should route back to, e.g. 'list' or 'mine'
82     @page = page_index + 1 # nice 1-based external page numbers
83   end
84
85   def mine
86     if @user
87       list(@user, 'mine') unless @user.nil?
88     else
89       redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri
90     end
91   end
92
93   def view
94     @trace = Trace.find(params[:id])
95     unless @trace.public
96       if @user
97         render :nothing, :status => 401 if @trace.user.id != @user.id
98       end
99     end
100   end
101
102   def create
103     filename = "/tmp/#{rand}"
104
105     File.open(filename, "w") { |f| f.write(params[:trace][:gpx_file].read) }
106     params[:trace][:name] = params[:trace][:gpx_file].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
107     params[:trace].delete('gpx_file') # remove the field from the hash, because there's no such field in the DB
108     @trace = Trace.new(params[:trace])
109     @trace.inserted = false
110     @trace.user = @user
111     @trace.timestamp = Time.now
112
113     if @trace.save
114       saved_filename = "/home/osm/gpx/#{@trace.id}.gpx"
115       File.rename(filename, saved_filename)
116
117       logger.info("id is #{@trace.id}")
118       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."
119       redirect_to :action => 'mine'
120     end
121   end
122
123   def data
124     trace = Trace.find(params[:id])
125     if trace and (trace.public? or (@user and @user == trace.user))
126       send_file(trace.trace_name, :filename => "#{trace.id}.gpx", :type => trace.mime_type, :disposition => 'attachment')
127     else
128       render :nothing, :status => 404
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/rss+xml'
153
154     render :text => rss.to_s
155   end
156
157   def picture
158     trace = Trace.find(params[:id])
159     if trace and (trace.public? or (@user and @user == trace.user))
160       send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline')
161     else
162       render :nothing, :status => 404
163     end
164   end
165
166   def icon
167     trace = Trace.find(params[:id])
168     if trace and (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 => 404
172     end
173   end
174
175   def api_details
176     trace = Trace.find(params[:id])
177     doc = OSM::API.new.get_xml_doc
178     doc.root << trace.to_xml_node() if trace.public? or trace.user == @user
179     render :text => doc.to_s
180   end
181
182   def api_data
183     render :action => 'data'
184   end
185
186   def api_create
187     #FIXME merge this code with create as they're pretty similar?
188     
189     filename = "/tmp/#{rand}"
190     File.open(filename, "w") { |f| f.write(request.raw_post) }
191     params[:trace] = {}
192     params[:trace][:name] = params[:filename]
193     params[:trace][:tagstring] = params[:tags]
194     params[:trace][:description] = params[:description]
195     @trace = Trace.new(params[:trace])
196     @trace.inserted = false
197     @trace.user = @user
198     @trace.timestamp = Time.now
199
200     if @trace.save
201       saved_filename = "/home/osm/gpx/#{@trace.id}.gpx"
202       File.rename(filename, saved_filename)
203       logger.info("id is #{@trace.id}")
204       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."
205       render :nothing => true
206     else
207       render :nothing => true, :status => 400 # er FIXME what fricking code to return?
208     end
209
210   end
211 end