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