2 class NotesController < ApiController
3 before_action :check_api_readable
4 before_action :check_api_writable, :only => [:create, :comment, :close, :reopen, :destroy]
5 before_action :setup_user_auth, :only => [:create, :show]
6 before_action :authorize, :only => [:close, :reopen, :destroy, :comment]
10 before_action :set_locale
11 around_action :api_call_handle_error, :api_call_timeout
12 before_action :set_request_formats, :except => [:feed]
15 # Return a list of notes in a given area
17 # Figure out the bbox - we prefer a bbox argument but also
18 # support the old, deprecated, method with four arguments
20 bbox = BoundingBox.from_bbox_params(params)
21 elsif params[:l] && params[:r] && params[:b] && params[:t]
22 bbox = BoundingBox.from_lrbt_params(params)
24 raise OSM::APIBadUserInput, "The parameter bbox is required"
27 # Get any conditions that need to be applied
28 notes = closed_condition(Note.all)
30 # Check that the boundaries are valid
33 # Check the the bounding box is not too big
34 bbox.check_size(Settings.max_note_request_area)
35 @min_lon = bbox.min_lon
36 @min_lat = bbox.min_lat
37 @max_lon = bbox.max_lon
38 @max_lat = bbox.max_lat
40 # Find the notes we want to return
41 @notes = notes.bbox(bbox).order("updated_at DESC").limit(result_limit).preload(:comments)
44 respond_to do |format|
55 # Check the arguments are sane
56 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
58 # Find the note and check it is valid
59 @note = Note.find(params[:id])
60 raise OSM::APINotFoundError unless @note
61 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user&.moderator?
64 respond_to do |format|
76 raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
78 # Check the arguments are sane
79 raise OSM::APIBadUserInput, "No lat was given" unless params[:lat]
80 raise OSM::APIBadUserInput, "No lon was given" unless params[:lon]
81 raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
83 # Extract the arguments
84 lon = OSM.parse_float(params[:lon], OSM::APIBadUserInput, "lon was not a number")
85 lat = OSM.parse_float(params[:lat], OSM::APIBadUserInput, "lat was not a number")
86 comment = params[:text]
88 # Include in a transaction to ensure that there is always a note_comment for every note
91 @note = Note.create(:lat => lat, :lon => lon)
92 raise OSM::APIBadUserInput, "The note is outside this world" unless @note.in_world?
97 # Add a comment to the note
98 add_comment(@note, comment, "opened")
101 # Return a copy of the new note
102 respond_to do |format|
103 format.xml { render :action => :show }
104 format.json { render :action => :show }
109 # Delete (hide) a note
111 # Check the arguments are sane
112 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
114 # Extract the arguments
115 id = params[:id].to_i
116 comment = params[:text]
118 # Find the note and check it is valid
119 @note = Note.find(id)
120 raise OSM::APINotFoundError unless @note
121 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
123 # Mark the note as hidden
125 @note.status = "hidden"
128 add_comment(@note, comment, "hidden", :notify => false)
131 # Return a copy of the updated note
132 respond_to do |format|
133 format.xml { render :action => :show }
134 format.json { render :action => :show }
139 # Add a comment to an existing note
142 raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
144 # Check the arguments are sane
145 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
146 raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
148 # Extract the arguments
149 id = params[:id].to_i
150 comment = params[:text]
152 # Find the note and check it is valid
153 @note = Note.find(id)
154 raise OSM::APINotFoundError unless @note
155 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
156 raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
158 # Add a comment to the note
160 add_comment(@note, comment, "commented")
163 # Return a copy of the updated note
164 respond_to do |format|
165 format.xml { render :action => :show }
166 format.json { render :action => :show }
173 # Check the arguments are sane
174 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
176 # Extract the arguments
177 id = params[:id].to_i
178 comment = params[:text]
180 # Find the note and check it is valid
181 @note = Note.find_by(:id => id)
182 raise OSM::APINotFoundError unless @note
183 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
184 raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
186 # Close the note and add a comment
190 add_comment(@note, comment, "closed")
193 # Return a copy of the updated note
194 respond_to do |format|
195 format.xml { render :action => :show }
196 format.json { render :action => :show }
203 # Check the arguments are sane
204 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
206 # Extract the arguments
207 id = params[:id].to_i
208 comment = params[:text]
210 # Find the note and check it is valid
211 @note = Note.find_by(:id => id)
212 raise OSM::APINotFoundError unless @note
213 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user.moderator?
214 raise OSM::APINoteAlreadyOpenError, @note unless @note.closed? || !@note.visible?
216 # Reopen the note and add a comment
220 add_comment(@note, comment, "reopened")
223 # Return a copy of the updated note
224 respond_to do |format|
225 format.xml { render :action => :show }
226 format.json { render :action => :show }
231 # Get a feed of recent notes and comments
233 # Get any conditions that need to be applied
234 notes = closed_condition(Note.all)
235 notes = bbox_condition(notes)
237 # Find the comments we want to return
238 @comments = NoteComment.where(:note => notes)
239 .order(:created_at => :desc).limit(result_limit)
240 .preload(:author, :note => { :comments => :author })
243 respond_to do |format|
249 # Return a list of notes matching a given string
251 # Get the initial set of notes
252 @notes = closed_condition(Note.all)
253 @notes = bbox_condition(@notes)
255 # Add any user filter
256 if params[:display_name] || params[:user]
257 if params[:display_name]
258 @user = User.find_by(:display_name => params[:display_name])
260 raise OSM::APIBadUserInput, "User #{params[:display_name]} not known" unless @user
262 @user = User.find_by(:id => params[:user])
264 raise OSM::APIBadUserInput, "User #{params[:user]} not known" unless @user
267 @notes = @notes.joins(:comments).where(:note_comments => { :author_id => @user })
270 # Add any text filter
271 @notes = @notes.joins(:comments).where("to_tsvector('english', note_comments.body) @@ plainto_tsquery('english', ?)", params[:q]) if params[:q]
273 # Add any date filter
276 from = Time.parse(params[:from]).utc
278 raise OSM::APIBadUserInput, "Date #{params[:from]} is in a wrong format"
283 Time.parse(params[:to]).utc
288 raise OSM::APIBadUserInput, "Date #{params[:to]} is in a wrong format"
291 @notes = if params[:sort] == "updated_at"
292 @notes.where(:updated_at => from..to)
294 @notes.where(:created_at => from..to)
298 # Choose the sort order
299 @notes = if params[:sort] == "created_at"
300 if params[:order] == "oldest"
301 @notes.order("created_at ASC")
303 @notes.order("created_at DESC")
306 if params[:order] == "oldest"
307 @notes.order("updated_at ASC")
309 @notes.order("updated_at DESC")
313 # Find the notes we want to return
314 @notes = @notes.distinct.limit(result_limit).preload(:comments)
317 respond_to do |format|
318 format.rss { render :action => :index }
319 format.xml { render :action => :index }
320 format.json { render :action => :index }
321 format.gpx { render :action => :index }
327 #------------------------------------------------------------
328 # utility functions below.
329 #------------------------------------------------------------
332 # Get the maximum number of results to return
335 if params[:limit].to_i.positive? && params[:limit].to_i <= Settings.max_note_query_limit
338 raise OSM::APIBadUserInput, "Note limit must be between 1 and #{Settings.max_note_query_limit}"
341 Settings.default_note_query_limit
346 # Generate a condition to choose which notes we want based
347 # on their status and the user's request parameters
348 def closed_condition(notes)
349 closed_since = if params[:closed]
350 params[:closed].to_i.days
352 Note::DEFAULT_FRESHLY_CLOSED_LIMIT
355 if closed_since.negative?
356 notes.where.not(:status => "hidden")
357 elsif closed_since.positive?
358 notes.where(:status => "open")
359 .or(notes.where(:status => "closed")
360 .where(notes.arel_table[:closed_at].gt(Time.now.utc - closed_since)))
362 notes.where(:status => "open")
367 # Generate a condition to choose which notes we want based
368 # on the user's bounding box request parameters
369 def bbox_condition(notes)
371 bbox = BoundingBox.from_bbox_params(params)
373 bbox.check_boundaries
374 bbox.check_size(Settings.max_note_request_area)
376 @min_lon = bbox.min_lon
377 @min_lat = bbox.min_lat
378 @max_lon = bbox.max_lon
379 @max_lat = bbox.max_lat
388 # Add a comment to a note
389 def add_comment(note, text, event, notify: true)
390 attributes = { :visible => true, :event => event, :body => text }
393 attributes[:author_id] = current_user.id
395 attributes[:author_ip] = request.remote_ip
398 comment = note.comments.create!(attributes)
400 note.comments.map(&:author).uniq.each do |user|
401 UserMailer.note_comment_notification(comment, user).deliver_later if notify && user && user != current_user && user.visible?