API 0.4 Updates - work on traces pages + pagination, edit tab, some API testing
[rails.git] / app / controllers / trace_controller.rb
1 class TraceController < ApplicationController
2   before_filter :authorize_web  
3   layout 'site'
4   
5   # Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
6   #  target_user - if set, specifies the user to fetch traces for.  if not set will fetch all traces
7   #  paging_action - the action that will be linked back to from view
8   def list (target_user = nil, paging_action = 'list')
9     @traces_per_page = 4
10     page_index = params[:page] ? params[:page].to_i - 1 : 0 # nice 1-based page -> 0-based page index
11
12     # from display name, pick up user id if one user's traces only
13     display_name = params[:display_name]
14     if target_user.nil? and display_name and display_name != ''
15       target_user = User.find(:first, :conditions => [ "display_name = ?", display_name])
16     end
17
18     opt = Hash.new
19     opt[:include] = [:user, :tags] # load users and tags from db at same time as traces
20
21     # four main cases:
22     # 1 - all traces, logged in = all public traces + all user's (i.e + all mine)
23     # 2 - all traces, not logged in = all public traces
24     # 3 - user's traces, logged in as same user = all user's traces 
25     # 4 - user's traces, not logged in as that user = all user's public traces
26     if target_user.nil? # all traces
27       if @user
28         conditions = ["(public = 1 OR user_id = ?)", @user.id] #1
29       else
30         conditions  = ["public = 1"] #2
31       end
32     else
33       if @user and @user.id == target_user.id
34         conditions = ["user_id = ?", @user.id] #3 (check vs user id, so no join + can't pick up non-public traces by changing name)
35       else
36         conditions = ["public = 1 AND user_id = ?", target_user.id] #4
37       end
38     end
39     conditions[0] += " AND users.display_name != ''" # users need to set display name before traces will be exposed
40     
41     opt[:order] = 'timestamp DESC'
42     if params[:tag]
43       conditions[0] += " AND gpx_file_tags.tag = ?"
44       conditions << params[:tag];
45     end
46     
47     opt[:conditions] = conditions
48
49     # count traces using all options except limit
50     @max_trace = Trace.count(opt)
51     @max_page = Integer((@max_trace + 1) / @traces_per_page) 
52     
53     # last step before fetch - add paging options
54     opt[:limit] = @traces_per_page
55     if page_index > 0
56       opt[:offset] = @traces_per_page * page_index
57     end
58
59     @traces = Trace.find(:all , opt)
60     
61     # put together SET of tags across traces, for related links
62     tagset = Hash.new
63     if @traces
64       @traces.each do |trace|
65         trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
66         trace.tags.each do |tag|
67           tagset[tag.tag] = tag.tag
68         end
69       end
70     end
71     
72     # final helper vars for view
73     @display_name = display_name
74     @all_tags = tagset.values
75     @paging_action = paging_action # the action that paging requests should route back to, e.g. 'list' or 'mine'
76     @page = page_index + 1 # nice 1-based external page numbers
77   end
78
79   def mine
80     if @user
81       list(@user, 'mine') unless @user.nil?
82     else
83       redirect_to :controller => 'user', :action => 'login'
84     end
85   end
86
87   def view
88     @trace = Trace.find(params[:id])
89     unless @trace.public
90       if @user
91         render :nothing, :status => 401 if @trace.user.id != @user.id
92       end
93     end
94   end
95
96   def create
97     filename = "/tmp/#{rand}"
98
99     File.open(filename, "w") { |f| f.write(@params['trace']['gpx_file'].read) }
100     @params['trace']['name'] = @params['trace']['gpx_file'].original_filename.gsub(/[^a-zA-Z0-9.]/, '_') # This makes sure filenames are sane
101     @params['trace'].delete('gpx_file') # let's remove the field from the hash, because there's no such field in the DB anyway.
102     @trace = Trace.new(@params['trace'])
103     @trace.inserted = false
104     @trace.user_id = @user.id
105     @trace.timestamp = Time.now
106     if @trace.save
107       logger.info("id is #{@trace.id}")
108       File.rename(filename, "/tmp/#{@trace.id}.gpx")
109       # *nix - specific `mv #{filename} /tmp/#{@trace.id}.gpx`
110       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."
111     end
112
113     redirect_to :action => 'mine'
114   end
115
116   def georss
117     traces = Trace.find(:all, :conditions => ['public = true'], :order => 'timestamp DESC', :limit => 20)
118
119     rss = OSM::GeoRSS.new
120
121     #def add(latitude=0, longitude=0, title_text='dummy title', url='http://www.example.com/', description_text='dummy description', timestamp=Time.now)
122     traces.each do |trace|
123       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)
124     end
125
126     response.headers["Content-Type"] = 'application/xml+rss'
127
128     render :text => rss.to_s
129   end
130
131   def picture
132     trace = Trace.find(params[:id])
133     send_data(trace.large_picture, :filename => "#{trace.id}.gif", :type => 'image/gif', :disposition => 'inline') if trace.public
134   end
135
136   def icon
137     trace = Trace.find(params[:id])
138     send_data(trace.icon_picture, :filename => "#{trace.id}_icon.gif", :type => 'image/gif', :disposition => 'inline') if trace.public
139   end
140 end