Disable notes search until it can be made scalable
[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 arguments are sane
57     raise OSM::APIBadUserInput.new("No lat was given") unless params[:lat]
58     raise OSM::APIBadUserInput.new("No lon was given") unless params[:lon]
59     raise OSM::APIBadUserInput.new("No text was given") if params[:text].blank?
60
61     # Extract the arguments
62     lon = OSM.parse_float(params[:lon], OSM::APIBadUserInput, "lon was not a number")
63     lat = OSM.parse_float(params[:lat], OSM::APIBadUserInput, "lat was not a number")
64     comment = params[:text]
65
66     # Include in a transaction to ensure that there is always a note_comment for every note
67     Note.transaction do
68       # Create the note
69       @note = Note.create(:lat => lat, :lon => lon)
70       raise OSM::APIBadUserInput.new("The note is outside this world") unless @note.in_world?
71
72       # Save the note
73       @note.save!
74
75       # Add a comment to the note
76       add_comment(@note, comment, "opened")
77     end
78
79     # Return a copy of the new note
80     respond_to do |format|
81       format.xml { render :action => :show }
82       format.json { render :action => :show }
83     end
84   end
85
86   ##
87   # Add a comment to an existing note
88   def comment
89     # Check the arguments are sane
90     raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
91     raise OSM::APIBadUserInput.new("No text was given") if params[:text].blank?
92
93     # Extract the arguments
94     id = params[:id].to_i
95     comment = params[:text]
96
97     # Find the note and check it is valid
98     @note = Note.find(id)
99     raise OSM::APINotFoundError unless @note
100     raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
101     raise OSM::APINoteAlreadyClosedError.new(@note) if @note.closed?
102
103     # Add a comment to the note
104     Note.transaction do
105       add_comment(@note, comment, "commented")
106     end
107
108     # Return a copy of the updated note
109     respond_to do |format|
110       format.xml { render :action => :show }
111       format.json { render :action => :show }
112     end
113   end
114
115   ##
116   # Close a note
117   def close
118     # Check the arguments are sane
119     raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
120
121     # Extract the arguments
122     id = params[:id].to_i
123     comment = params[:text]
124
125     # Find the note and check it is valid
126     @note = Note.find_by_id(id)
127     raise OSM::APINotFoundError unless @note
128     raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
129     raise OSM::APINoteAlreadyClosedError.new(@note) if @note.closed?
130
131     # Close the note and add a comment
132     Note.transaction do
133       @note.close
134
135       add_comment(@note, comment, "closed")
136     end
137
138     # Return a copy of the updated note
139     respond_to do |format|
140       format.xml { render :action => :show }
141       format.json { render :action => :show }
142     end
143   end 
144
145   ##
146   # Reopen a note
147   def reopen
148     # Check the arguments are sane
149     raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
150
151     # Extract the arguments
152     id = params[:id].to_i
153     comment = params[:text]
154
155     # Find the note and check it is valid
156     @note = Note.find_by_id(id)
157     raise OSM::APINotFoundError unless @note
158     raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? or @user.moderator?
159     raise OSM::APINoteAlreadyOpenError.new(@note) unless @note.closed? or not @note.visible?
160
161     # Reopen the note and add a comment
162     Note.transaction do
163       @note.reopen
164
165       add_comment(@note, comment, "reopened")
166     end
167
168     # Return a copy of the updated note
169     respond_to do |format|
170       format.xml { render :action => :show }
171       format.json { render :action => :show }
172     end
173   end 
174
175   ##
176   # Get a feed of recent notes and comments
177   def feed
178     # Get any conditions that need to be applied
179     notes = closed_condition(Note.all)
180
181     # Process any bbox
182     if params[:bbox]
183       bbox = BoundingBox.from_bbox_params(params)
184
185       bbox.check_boundaries
186       bbox.check_size(MAX_NOTE_REQUEST_AREA)
187
188       notes = notes.bbox(bbox)
189     end
190
191     # Find the comments we want to return
192     @comments = NoteComment.where(:note_id => notes).order("created_at DESC").limit(result_limit).preload(:note)
193
194     # Render the result
195     respond_to do |format|
196       format.rss
197     end
198   end
199
200   ##
201   # Read a note
202   def show
203     # Check the arguments are sane
204     raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
205
206     # Find the note and check it is valid
207     @note = Note.find(params[:id])
208     raise OSM::APINotFoundError unless @note
209     raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
210
211     # Render the result
212     respond_to do |format|
213       format.xml
214       format.rss
215       format.json
216       format.gpx
217     end
218   end
219
220   ##
221   # Delete (hide) a note
222   def destroy
223     # Check the arguments are sane
224     raise OSM::APIBadUserInput.new("No id was given") unless params[:id]
225
226     # Extract the arguments
227     id = params[:id].to_i
228     comment = params[:text]
229
230     # Find the note and check it is valid
231     @note = Note.find(id)
232     raise OSM::APINotFoundError unless @note
233     raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
234
235     # Mark the note as hidden
236     Note.transaction do
237       @note.status = "hidden"
238       @note.save
239
240       add_comment(@note, comment, "hidden", false)
241     end
242
243     # Return a copy of the updated note
244     respond_to do |format|
245       format.xml { render :action => :show }
246       format.json { render :action => :show }
247     end
248   end
249
250   ##
251   # Return a list of notes matching a given string
252   def search
253     # Check the arguments are sane
254     raise OSM::APIBadUserInput.new("No query string was given") unless params[:q]
255
256     # Get any conditions that need to be applied
257     @notes = closed_condition(Note.all)
258     @notes = @notes.joins(:comments).where("note_comments.body ~ ?", params[:q])
259
260     # Find the notes we want to return
261     @notes = @notes.order("updated_at DESC").limit(result_limit).preload(:comments)
262
263     # Disable notes search until we can make it scalable
264     response.headers['Error'] = "Searching of notes is currently unavailable"
265     render :text => "", :status => :service_unavailable
266     return false
267
268     # Render the result
269     respond_to do |format|
270       format.rss { render :action => :index }
271       format.xml { render :action => :index }
272       format.json { render :action => :index }
273       format.gpx { render :action => :index }
274     end
275   end
276
277   ##
278   # Display a list of notes by a specified user
279   def mine
280     if params[:display_name] 
281       if @this_user = User.active.find_by_display_name(params[:display_name])
282         @title =  t 'note.mine.title', :user => @this_user.display_name 
283         @heading =  t 'note.mine.heading', :user => @this_user.display_name 
284         @description = t 'note.mine.subheading', :user => render_to_string(:partial => "user", :object => @this_user)
285         @page = (params[:page] || 1).to_i 
286         @page_size = 10
287         @notes = @this_user.notes.order("updated_at DESC, id").uniq.offset((@page - 1) * @page_size).limit(@page_size).preload(:comments => :author).to_a
288       else
289         @title = t 'user.no_such_user.title' 
290         @not_found_user = params[:display_name] 
291
292         render :template => 'user/no_such_user', :status => :not_found 
293       end 
294     end
295   end
296
297 private 
298   #------------------------------------------------------------ 
299   # utility functions below. 
300   #------------------------------------------------------------   
301  
302   ##
303   # Render an OK response
304   def render_ok
305     if params[:format] == "js"
306       render :text => "osbResponse();", :content_type => "text/javascript" 
307     else
308       render :text => "ok " + @note.id.to_s + "\n", :content_type => "text/plain" if @note
309       render :text => "ok\n", :content_type => "text/plain" unless @note
310     end
311   end
312
313   ##
314   # Get the maximum number of results to return
315   def result_limit
316     if params[:limit]
317       if params[:limit].to_i > 0 and params[:limit].to_i < 10000
318         params[:limit].to_i
319       else
320         raise OSM::APIBadUserInput.new("Note limit must be between 1 and 9999")
321       end
322     else
323       100
324     end
325   end
326
327   ##
328   # Generate a condition to choose which bugs we want based
329   # on their status and the user's request parameters
330   def closed_condition(notes)
331     if params[:closed]
332       closed_since = params[:closed].to_i
333     else
334       closed_since = 7
335     end
336         
337     if closed_since < 0
338       notes = notes.where("status != 'hidden'")
339     elsif closed_since > 0
340       notes = notes.where("(status = 'open' OR (status = 'closed' AND closed_at > '#{Time.now - closed_since.days}'))")
341     else
342       notes = notes.where("status = 'open'")
343     end
344
345     return notes
346   end
347
348   ##
349   # Add a comment to a note
350   def add_comment(note, text, event, notify = true)
351     attributes = { :visible => true, :event => event, :body => text }
352
353     if @user  
354       attributes[:author_id] = @user.id
355     else  
356       attributes[:author_ip] = request.remote_ip
357     end
358
359     comment = note.comments.create(attributes)
360
361     note.comments.map { |c| c.author }.uniq.each do |user|
362       if notify and user and user != @user
363         Notifier.note_comment_notification(comment, user).deliver
364       end
365     end
366   end
367 end