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