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 user = query_conditions_user_value
260 @notes = @notes.joins(:comments).where(:note_comments => { :author_id => user }) if user
262 # Add any text filter
264 @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])
267 # Add any date filter
268 time_filter_property = if params[:sort] == "updated_at"
273 @notes = query_conditions_time(@notes, time_filter_property)
275 # Choose the sort order
276 @notes = if params[:sort] == "created_at"
277 if params[:order] == "oldest"
278 @notes.order("created_at ASC")
280 @notes.order("created_at DESC")
283 if params[:order] == "oldest"
284 @notes.order("updated_at ASC")
286 @notes.order("updated_at DESC")
290 # Find the notes we want to return
291 @notes = query_limit(@notes.distinct)
292 @notes = @notes.preload(:comments)
295 respond_to do |format|
296 format.rss { render :action => :index }
297 format.xml { render :action => :index }
298 format.json { render :action => :index }
299 format.gpx { render :action => :index }
305 #------------------------------------------------------------
306 # utility functions below.
307 #------------------------------------------------------------
310 # Generate a condition to choose which notes we want based
311 # on their status and the user's request parameters
312 def closed_condition(notes)
313 closed_since = if params[:closed]
314 params[:closed].to_i.days
316 Note::DEFAULT_FRESHLY_CLOSED_LIMIT
319 if closed_since.negative?
320 notes.where.not(:status => "hidden")
321 elsif closed_since.positive?
322 notes.where(:status => "open")
323 .or(notes.where(:status => "closed")
324 .where(notes.arel_table[:closed_at].gt(Time.now.utc - closed_since)))
326 notes.where(:status => "open")
331 # Generate a condition to choose which notes we want based
332 # on the user's bounding box request parameters
333 def bbox_condition(notes)
335 bbox = BoundingBox.from_bbox_params(params)
337 bbox.check_boundaries
338 bbox.check_size(Settings.max_note_request_area)
340 @min_lon = bbox.min_lon
341 @min_lat = bbox.min_lat
342 @max_lon = bbox.max_lon
343 @max_lat = bbox.max_lat
352 # Get author's information (for logged in users - user_id, for logged out users - IP address)
355 { :user_id => current_user.id }
357 { :user_ip => request.remote_ip }
362 # Add a comment to a note
363 def add_comment(note, text, event, notify: true)
364 attributes = { :visible => true, :event => event, :body => text }
366 # Get note comment's author info (for logged in users - user_id, for logged out users - IP address)
367 note_comment_author_info = author_info
369 if note_comment_author_info[:user_ip].nil?
370 attributes[:author_id] = note_comment_author_info[:user_id]
372 attributes[:author_ip] = note_comment_author_info[:user_ip]
375 comment = note.comments.create!(attributes)
378 note.subscribers.visible.each do |user|
379 UserMailer.note_comment_notification(comment, user).deliver_later if current_user != user
383 NoteSubscription.find_or_create_by(:note => note, :user => current_user) if current_user