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