2 class NotesController < ApiController
5 before_action :check_api_writable, :only => [:create, :comment, :close, :reopen, :destroy]
6 before_action :setup_user_auth, :only => [:create, :show]
7 before_action :authorize, :only => [:close, :reopen, :destroy, :comment]
11 before_action :set_locale
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")
42 notes = query_limit(notes)
43 @notes = notes.preload(:comments)
46 respond_to do |format|
57 # Check the arguments are sane
58 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
60 # Find the note and check it is valid
61 @note = Note.find(params[:id])
62 raise OSM::APINotFoundError unless @note
63 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user&.moderator?
66 respond_to do |format|
78 raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
80 # Check the arguments are sane
81 raise OSM::APIBadUserInput, "No lat was given" unless params[:lat]
82 raise OSM::APIBadUserInput, "No lon was given" unless params[:lon]
83 raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
85 # Extract the arguments
86 lon = OSM.parse_float(params[:lon], OSM::APIBadUserInput, "lon was not a number")
87 lat = OSM.parse_float(params[:lat], OSM::APIBadUserInput, "lat was not a number")
88 description = params[:text]
90 # Get note's author info (for logged in users - user_id, for logged out users - IP address)
91 note_author_info = author_info
93 # Include in a transaction to ensure that there is always a note_comment for every note
96 @note = Note.create(:lat => lat, :lon => lon, :description => description, :user_id => note_author_info[:user_id], :user_ip => note_author_info[:user_ip])
97 raise OSM::APIBadUserInput, "The note is outside this world" unless @note.in_world?
102 # Add opening comment (description) to the note
103 add_comment(@note, description, "opened")
106 # Return a copy of the new note
107 respond_to do |format|
108 format.xml { render :action => :show }
109 format.json { render :action => :show }
114 # Delete (hide) a note
116 # Check the arguments are sane
117 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
119 # Extract the arguments
120 id = params[:id].to_i
121 comment = params[:text]
123 # Find the note and check it is valid
125 @note = Note.lock.find(id)
126 raise OSM::APINotFoundError unless @note
127 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
129 # Mark the note as hidden
130 @note.status = "hidden"
133 add_comment(@note, comment, "hidden", :notify => false)
136 # Return a copy of the updated note
137 respond_to do |format|
138 format.xml { render :action => :show }
139 format.json { render :action => :show }
144 # Add a comment to an existing note
146 # Check the arguments are sane
147 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
148 raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
150 # Extract the arguments
151 id = params[:id].to_i
152 comment = params[:text]
154 # Find the note and check it is valid
156 @note = Note.lock.find(id)
157 raise OSM::APINotFoundError unless @note
158 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
159 raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
161 # Add a comment to the note
162 add_comment(@note, comment, "commented")
165 # Return a copy of the updated note
166 respond_to do |format|
167 format.xml { render :action => :show }
168 format.json { render :action => :show }
175 # Check the arguments are sane
176 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
178 # Extract the arguments
179 id = params[:id].to_i
180 comment = params[:text]
182 # Find the note and check it is valid
184 @note = Note.lock.find_by(:id => id)
185 raise OSM::APINotFoundError unless @note
186 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
187 raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
189 # Close the note and add a comment
192 add_comment(@note, comment, "closed")
195 # Return a copy of the updated note
196 respond_to do |format|
197 format.xml { render :action => :show }
198 format.json { render :action => :show }
205 # Check the arguments are sane
206 raise OSM::APIBadUserInput, "No id was given" unless params[:id]
208 # Extract the arguments
209 id = params[:id].to_i
210 comment = params[:text]
212 # Find the note and check it is valid
214 @note = Note.lock.find_by(:id => id)
215 raise OSM::APINotFoundError unless @note
216 raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user.moderator?
217 raise OSM::APINoteAlreadyOpenError, @note unless @note.closed? || !@note.visible?
219 # Reopen the note and add a comment
222 add_comment(@note, comment, "reopened")
225 # Return a copy of the updated note
226 respond_to do |format|
227 format.xml { render :action => :show }
228 format.json { render :action => :show }
233 # Get a feed of recent notes and comments
235 # Get any conditions that need to be applied
236 notes = closed_condition(Note.all)
237 notes = bbox_condition(notes)
239 # Find the comments we want to return
240 @comments = NoteComment.where(:note => notes)
241 .order(:created_at => :desc)
242 @comments = query_limit(@comments)
243 @comments = @comments.preload(:author, :note => { :comments => :author })
246 respond_to do |format|
252 # Return a list of notes matching a given string
254 # Get the initial set of notes
255 @notes = closed_condition(Note.all)
256 @notes = bbox_condition(@notes)
258 # Add any user filter
259 if params[:display_name] || params[:user]
260 if params[:display_name]
261 @user = User.find_by(:display_name => params[:display_name])
263 raise OSM::APIBadUserInput, "User #{params[:display_name]} not known" unless @user
265 @user = User.find_by(:id => params[:user])
267 raise OSM::APIBadUserInput, "User #{params[:user]} not known" unless @user
270 @notes = @notes.joins(:comments).where(:note_comments => { :author_id => @user })
273 # Add any text filter
275 @notes = @notes.joins(:comments).where("to_tsvector('english', note_comments.body) @@ plainto_tsquery('english', ?) OR to_tsvector('english', notes.description) @@ plainto_tsquery('english', ?)", params[:q], params[:q])
278 # Add any date filter
281 from = Time.parse(params[:from]).utc
283 raise OSM::APIBadUserInput, "Date #{params[:from]} is in a wrong format"
288 Time.parse(params[:to]).utc
293 raise OSM::APIBadUserInput, "Date #{params[:to]} is in a wrong format"
296 @notes = if params[:sort] == "updated_at"
297 @notes.where(:updated_at => from..to)
299 @notes.where(:created_at => from..to)
303 # Choose the sort order
304 @notes = if params[:sort] == "created_at"
305 if params[:order] == "oldest"
306 @notes.order("created_at ASC")
308 @notes.order("created_at DESC")
311 if params[:order] == "oldest"
312 @notes.order("updated_at ASC")
314 @notes.order("updated_at DESC")
318 # Find the notes we want to return
319 @notes = query_limit(@notes.distinct)
320 @notes = @notes.preload(:comments)
323 respond_to do |format|
324 format.rss { render :action => :index }
325 format.xml { render :action => :index }
326 format.json { render :action => :index }
327 format.gpx { render :action => :index }
333 #------------------------------------------------------------
334 # utility functions below.
335 #------------------------------------------------------------
338 # Generate a condition to choose which notes we want based
339 # on their status and the user's request parameters
340 def closed_condition(notes)
341 closed_since = if params[:closed]
342 params[:closed].to_i.days
344 Note::DEFAULT_FRESHLY_CLOSED_LIMIT
347 if closed_since.negative?
348 notes.where.not(:status => "hidden")
349 elsif closed_since.positive?
350 notes.where(:status => "open")
351 .or(notes.where(:status => "closed")
352 .where(notes.arel_table[:closed_at].gt(Time.now.utc - closed_since)))
354 notes.where(:status => "open")
359 # Generate a condition to choose which notes we want based
360 # on the user's bounding box request parameters
361 def bbox_condition(notes)
363 bbox = BoundingBox.from_bbox_params(params)
365 bbox.check_boundaries
366 bbox.check_size(Settings.max_note_request_area)
368 @min_lon = bbox.min_lon
369 @min_lat = bbox.min_lat
370 @max_lon = bbox.max_lon
371 @max_lat = bbox.max_lat
380 # Get author's information (for logged in users - user_id, for logged out users - IP address)
383 { :user_id => current_user.id }
385 { :user_ip => request.remote_ip }
390 # Add a comment to a note
391 def add_comment(note, text, event, notify: true)
392 attributes = { :visible => true, :event => event, :body => text }
394 # Get note comment's author info (for logged in users - user_id, for logged out users - IP address)
395 note_comment_author_info = author_info
397 if note_comment_author_info[:user_ip].nil?
398 attributes[:author_id] = note_comment_author_info[:user_id]
400 attributes[:author_ip] = note_comment_author_info[:user_ip]
403 comment = note.comments.create!(attributes)
406 note.subscribers.visible.each do |user|
407 UserMailer.note_comment_notification(comment, user).deliver_later if current_user != user
411 NoteSubscription.find_or_create_by(:note => note, :user => current_user) if current_user