]> git.openstreetmap.org Git - rails.git/blob - app/controllers/note_controller.rb
Merge branch 'master' into openstreetbugs
[rails.git] / app / controllers / note_controller.rb
1 class NoteController < ApplicationController
2
3   layout 'site', :only => [:mine]
4
5   before_filter :check_api_readable
6   before_filter :authorize_web, :only => [:create, :close, :update, :delete, :mine]
7   before_filter :check_api_writable, :only => [:create, :close, :update, :delete]
8   before_filter :set_locale, :only => [:mine]
9   after_filter :compress_output
10   around_filter :api_call_handle_error, :api_call_timeout
11
12   # Help methods for checking boundary sanity and area size
13   include MapBoundary
14
15   ##
16   # Return a list of notes in a given area
17   def list
18     # Figure out the bbox - we prefer a bbox argument but also
19     # support the old, deprecated, method with four arguments
20     if params[:bbox]
21       bbox = BoundingBox.from_bbox_params(params)
22     else
23       raise OSM::APIBadUserInput.new("No l was given") unless params[:l]
24       raise OSM::APIBadUserInput.new("No r was given") unless params[:r]
25       raise OSM::APIBadUserInput.new("No b was given") unless params[:b]
26       raise OSM::APIBadUserInput.new("No t was given") unless params[:t]
27
28       bbox = BoundingBox.from_lrbt_params(params)
29     end
30
31     # Get the sanitised boundaries
32     @min_lon, @min_lat, @max_lon, @max_lat = sanitise_boundaries(bbox)
33
34     # Get any conditions that need to be applied
35     notes = closed_condition(Note.scoped)
36
37     # Check that the boundaries are valid
38     bbox.check_boundaries
39
40     # Check the the bounding box is not too big
41     bbox.check_size(MAX_NOTE_REQUEST_AREA)
42
43     # Find the notes we want to return
44     @notes = notes.bbox(bbox).order("updated_at DESC").limit(result_limit).preload(:comments)
45
46     # Render the result
47     respond_to do |format|
48       format.rss
49       format.xml
50       format.json
51       format.gpx
52     end
53   end
54
55   ##
56   # Create a new note
57   def create
58     # Check the arguments are sane
59     raise OSM::APIBadUserInput.new("No lat was given") unless params[:lat]
60     raise OSM::APIBadUserInput.new("No lon was given") unless params[:lon]
61     raise OSM::APIBadUserInput.new("No text was given") unless params[:text]
62
63     # Extract the arguments
64     lon = params[:lon].to_f
65     lat = params[:lat].to_f
66     comment = params[:text]
67     name = params[:name]
68
69     # Include in a transaction to ensure that there is always a note_comment for every note
70     Note.transaction do
71       # Create the note
72       @note = Note.create(:lat => lat, :lon => lon)
73       raise OSM::APIBadUserInput.new("The note is outside this world") unless @note.in_world?
74
75       #TODO: move this into a helper function
76       begin
77         url = "http://nominatim.openstreetmap.org/reverse?lat=" + lat.to_s + "&lon=" + lon.to_s + "&zoom=16" 
78         response = REXML::Document.new(Net::HTTP.get(URI.parse(url))) 
79                 
80         if result = response.get_text("reversegeocode/result") 
81           @note.nearby_place = result.to_s 
82         else 
83           @note.nearby_place = "unknown"
84         end
85       rescue Exception => err
86         @note.nearby_place = "unknown"
87       end
88
89       # Save the note
90       @note.save
91
92       # Add a comment to the note
93       add_comment(@note, comment, name, "opened")
94     end
95
96     # Send an OK response
97     render_ok
98   end
99
100   ##
101   # Add a comment to an existing note
102   def update
103     # Check the arguments are sane
104     raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
105     raise OSM::APIBadUserInput.new("No text was given") unless params[:text]
106
107     # Extract the arguments
108     id = params[:id].to_i
109     comment = params[:text]
110     name = params[:name] or "NoName"
111
112     # Find the note and check it is valid
113     note = Note.find(id)
114     raise OSM::APINotFoundError unless note
115     raise OSM::APIAlreadyDeletedError unless note.visible?
116
117     # Add a comment to the note
118     Note.transaction do
119       add_comment(note, comment, name, "commented")
120     end
121
122     # Send an OK response
123     render_ok
124   end
125
126   ##
127   # Close a note
128   def close
129     # Check the arguments are sane
130     raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
131
132     # Extract the arguments
133     id = params[:id].to_i
134     name = params[:name]
135
136     # Find the note and check it is valid
137     note = Note.find_by_id(id)
138     raise OSM::APINotFoundError unless note
139     raise OSM::APIAlreadyDeletedError unless note.visible?
140
141     # Close the note and add a comment
142     Note.transaction do
143       note.close
144
145       add_comment(note, nil, name, "closed")
146     end
147
148     # Send an OK response
149     render_ok
150   end 
151
152   ##
153   # Get a feed of recent notes and comments
154   def rss
155     # Get any conditions that need to be applied
156     notes = closed_condition(Note.scoped)
157
158     # Process any bbox
159     if params[:bbox]
160       bbox = BoundingBox.from_bbox_params(params)
161
162       bbox.check_boundaries
163       bbox.check_size(MAX_NOTE_REQUEST_AREA)
164
165       notes = notes.bbox(bbox)
166     end
167
168     # Find the comments we want to return
169     @comments = NoteComment.where(:note => notes).order("created_at DESC").limit(result_limit).include(:note)
170
171     # Render the result
172     respond_to do |format|
173       format.rss
174     end
175   end
176
177   ##
178   # Read a note
179   def read
180     # Check the arguments are sane
181     raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
182
183     # Find the note and check it is valid
184     @note = Note.find(params[:id])
185     raise OSM::APINotFoundError unless @note
186     raise OSM::APIAlreadyDeletedError unless @note.visible?
187     
188     # Render the result
189     respond_to do |format|
190       format.xml
191       format.rss
192       format.json
193       format.gpx
194     end
195   end
196
197   ##
198   # Delete (hide) a note
199   def delete
200     # Check the arguments are sane
201     raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
202
203     # Extract the arguments
204     id = params[:id].to_i
205     name = params[:name]
206
207     # Find the note and check it is valid
208     note = Note.find(id)
209     raise OSM::APINotFoundError unless note
210     raise OSM::APIAlreadyDeletedError unless note.visible?
211
212     # Mark the note as hidden
213     Note.transaction do
214       note.status = "hidden"
215       note.save
216
217       add_comment(note, nil, name, "hidden")
218     end
219
220     # Render the result
221     render :text => "ok\n", :content_type => "text/html" 
222   end
223
224   ##
225   # Return a list of notes matching a given string
226   def search
227     # Check the arguments are sane
228     raise OSM::APIBadUserInput.new("No query string was given") unless params[:q]
229
230     # Get any conditions that need to be applied
231     conditions = closed_condition
232     conditions = cond_merge conditions, ['note_comments.body ~ ?', params[:q]]
233         
234     # Find the notes we want to return
235     @notes = Note.find(:all, 
236                        :conditions => conditions,
237                        :order => "updated_at DESC",
238                        :limit => result_limit,
239                        :joins => :comments,
240                        :include => :comments)
241
242     # Render the result
243     respond_to do |format|
244       format.html { render :action => :list, :format => :rjs, :content_type => "text/javascript"}
245       format.rss { render :action => :list }
246       format.js
247       format.xml { render :action => :list }
248       format.json { render :action => :list }
249       format.gpx { render :action => :list }
250     end
251   end
252
253   def mine
254     if params[:display_name] 
255       @user2 = User.find_by_display_name(params[:display_name], :conditions => { :status => ["active", "confirmed"] }) 
256  
257       if @user2  
258         if @user2.data_public? or @user2 == @user 
259           conditions = ['note_comments.author_id = ?', @user2.id] 
260         else 
261           conditions = ['false'] 
262         end 
263       else #if request.format == :html 
264         @title = t 'user.no_such_user.title' 
265         @not_found_user = params[:display_name] 
266         render :template => 'user/no_such_user', :status => :not_found 
267         return
268       end 
269     end
270
271     if @user2 
272       user_link = render_to_string :partial => "user", :object => @user2 
273     end 
274     
275     @title =  t 'note.mine.title', :user => @user2.display_name 
276     @heading =  t 'note.mine.heading', :user => @user2.display_name 
277     @description = t 'note.mine.description', :user => user_link
278     
279     @page = (params[:page] || 1).to_i 
280     @page_size = 10
281
282     @notes = Note.find(:all, 
283                        :include => [:comments, {:comments => :author}],
284                        :joins => :comments,
285                        :order => "updated_at DESC",
286                        :conditions => conditions,
287                        :offset => (@page - 1) * @page_size, 
288                        :limit => @page_size).uniq
289   end
290
291 private 
292   #------------------------------------------------------------ 
293   # utility functions below. 
294   #------------------------------------------------------------   
295  
296   ##
297   # Render an OK response
298   def render_ok
299     if params[:format] == "js"
300       render :text => "osbResponse();", :content_type => "text/javascript" 
301     else
302       render :text => "ok " + @note.id.to_s + "\n", :content_type => "text/plain" if @note
303       render :text => "ok\n", :content_type => "text/plain" unless @note
304     end
305   end
306
307   ##
308   # Get the maximum number of results to return
309   def result_limit
310     if params[:limit] and params[:limit].to_i > 0 and params[:limit].to_i < 10000
311       params[:limit].to_i
312     else
313       100
314     end
315   end
316
317   ##
318   # Generate a condition to choose which bugs we want based
319   # on their status and the user's request parameters
320   def closed_condition(notes)
321     if params[:closed]
322       closed_since = params[:closed].to_i
323     else
324       closed_since = 7
325     end
326         
327     if closed_since < 0
328       notes = notes.where("status != 'hidden'")
329     elsif closed_since > 0
330       notes = notes.where("(status = 'open' OR (status = 'closed' AND closed_at > '#{Time.now - closed_since.days}'))")
331     else
332       notes = notes.where("status = 'open'")
333     end
334
335     return notes
336   end
337
338   ##
339   # Add a comment to a note
340   def add_comment(note, text, name, event)
341     name = "NoName" if name.nil?
342
343     attributes = { :visible => true, :event => event, :body => text }
344
345     if @user  
346       attributes[:author_id] = @user.id
347       attributes[:author_name] = @user.display_name
348     else  
349       attributes[:author_ip] = request.remote_ip
350       attributes[:author_name] = name + " (a)"
351     end
352
353     note.comments.create(attributes)
354
355     note.comments.map { |c| c.author }.uniq.each do |user|
356       if user and user != @user
357         Notifier.deliver_note_comment_notification(comment, user)
358       end
359     end
360   end
361 end