##// END OF EJS Templates
remove hard-coded '.rxml' from ApplicationController 'render_feed' (#6317)...
Toshi MARUYAMA -
r7453:2a27cc41f056
parent child
Show More
@@ -1,521 +1,522
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'uri'
18 require 'uri'
19 require 'cgi'
19 require 'cgi'
20
20
21 class Unauthorized < Exception; end
21 class Unauthorized < Exception; end
22
22
23 class ApplicationController < ActionController::Base
23 class ApplicationController < ActionController::Base
24 include Redmine::I18n
24 include Redmine::I18n
25
25
26 layout 'base'
26 layout 'base'
27 exempt_from_layout 'builder', 'rsb'
27 exempt_from_layout 'builder', 'rsb'
28
28
29 protect_from_forgery
29 protect_from_forgery
30 def handle_unverified_request
30 def handle_unverified_request
31 super
31 super
32 cookies.delete(:autologin)
32 cookies.delete(:autologin)
33 end
33 end
34 # Remove broken cookie after upgrade from 0.8.x (#4292)
34 # Remove broken cookie after upgrade from 0.8.x (#4292)
35 # See https://rails.lighthouseapp.com/projects/8994/tickets/3360
35 # See https://rails.lighthouseapp.com/projects/8994/tickets/3360
36 # TODO: remove it when Rails is fixed
36 # TODO: remove it when Rails is fixed
37 before_filter :delete_broken_cookies
37 before_filter :delete_broken_cookies
38 def delete_broken_cookies
38 def delete_broken_cookies
39 if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
39 if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
40 cookies.delete '_redmine_session'
40 cookies.delete '_redmine_session'
41 redirect_to home_path
41 redirect_to home_path
42 return false
42 return false
43 end
43 end
44 end
44 end
45
45
46 before_filter :user_setup, :check_if_login_required, :set_localization
46 before_filter :user_setup, :check_if_login_required, :set_localization
47 filter_parameter_logging :password
47 filter_parameter_logging :password
48
48
49 rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
49 rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
50 rescue_from ::Unauthorized, :with => :deny_access
50 rescue_from ::Unauthorized, :with => :deny_access
51
51
52 include Redmine::Search::Controller
52 include Redmine::Search::Controller
53 include Redmine::MenuManager::MenuController
53 include Redmine::MenuManager::MenuController
54 helper Redmine::MenuManager::MenuHelper
54 helper Redmine::MenuManager::MenuHelper
55
55
56 Redmine::Scm::Base.all.each do |scm|
56 Redmine::Scm::Base.all.each do |scm|
57 require_dependency "repository/#{scm.underscore}"
57 require_dependency "repository/#{scm.underscore}"
58 end
58 end
59
59
60 def user_setup
60 def user_setup
61 # Check the settings cache for each request
61 # Check the settings cache for each request
62 Setting.check_cache
62 Setting.check_cache
63 # Find the current user
63 # Find the current user
64 User.current = find_current_user
64 User.current = find_current_user
65 end
65 end
66
66
67 # Returns the current user or nil if no user is logged in
67 # Returns the current user or nil if no user is logged in
68 # and starts a session if needed
68 # and starts a session if needed
69 def find_current_user
69 def find_current_user
70 if session[:user_id]
70 if session[:user_id]
71 # existing session
71 # existing session
72 (User.active.find(session[:user_id]) rescue nil)
72 (User.active.find(session[:user_id]) rescue nil)
73 elsif cookies[:autologin] && Setting.autologin?
73 elsif cookies[:autologin] && Setting.autologin?
74 # auto-login feature starts a new session
74 # auto-login feature starts a new session
75 user = User.try_to_autologin(cookies[:autologin])
75 user = User.try_to_autologin(cookies[:autologin])
76 session[:user_id] = user.id if user
76 session[:user_id] = user.id if user
77 user
77 user
78 elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
78 elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
79 # RSS key authentication does not start a session
79 # RSS key authentication does not start a session
80 User.find_by_rss_key(params[:key])
80 User.find_by_rss_key(params[:key])
81 elsif Setting.rest_api_enabled? && accept_api_auth?
81 elsif Setting.rest_api_enabled? && accept_api_auth?
82 if (key = api_key_from_request)
82 if (key = api_key_from_request)
83 # Use API key
83 # Use API key
84 User.find_by_api_key(key)
84 User.find_by_api_key(key)
85 else
85 else
86 # HTTP Basic, either username/password or API key/random
86 # HTTP Basic, either username/password or API key/random
87 authenticate_with_http_basic do |username, password|
87 authenticate_with_http_basic do |username, password|
88 User.try_to_login(username, password) || User.find_by_api_key(username)
88 User.try_to_login(username, password) || User.find_by_api_key(username)
89 end
89 end
90 end
90 end
91 end
91 end
92 end
92 end
93
93
94 # Sets the logged in user
94 # Sets the logged in user
95 def logged_user=(user)
95 def logged_user=(user)
96 reset_session
96 reset_session
97 if user && user.is_a?(User)
97 if user && user.is_a?(User)
98 User.current = user
98 User.current = user
99 session[:user_id] = user.id
99 session[:user_id] = user.id
100 else
100 else
101 User.current = User.anonymous
101 User.current = User.anonymous
102 end
102 end
103 end
103 end
104
104
105 # check if login is globally required to access the application
105 # check if login is globally required to access the application
106 def check_if_login_required
106 def check_if_login_required
107 # no check needed if user is already logged in
107 # no check needed if user is already logged in
108 return true if User.current.logged?
108 return true if User.current.logged?
109 require_login if Setting.login_required?
109 require_login if Setting.login_required?
110 end
110 end
111
111
112 def set_localization
112 def set_localization
113 lang = nil
113 lang = nil
114 if User.current.logged?
114 if User.current.logged?
115 lang = find_language(User.current.language)
115 lang = find_language(User.current.language)
116 end
116 end
117 if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
117 if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
118 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first
118 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first
119 if !accept_lang.blank?
119 if !accept_lang.blank?
120 accept_lang = accept_lang.downcase
120 accept_lang = accept_lang.downcase
121 lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
121 lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
122 end
122 end
123 end
123 end
124 lang ||= Setting.default_language
124 lang ||= Setting.default_language
125 set_language_if_valid(lang)
125 set_language_if_valid(lang)
126 end
126 end
127
127
128 def require_login
128 def require_login
129 if !User.current.logged?
129 if !User.current.logged?
130 # Extract only the basic url parameters on non-GET requests
130 # Extract only the basic url parameters on non-GET requests
131 if request.get?
131 if request.get?
132 url = url_for(params)
132 url = url_for(params)
133 else
133 else
134 url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
134 url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
135 end
135 end
136 respond_to do |format|
136 respond_to do |format|
137 format.html { redirect_to :controller => "account", :action => "login", :back_url => url }
137 format.html { redirect_to :controller => "account", :action => "login", :back_url => url }
138 format.atom { redirect_to :controller => "account", :action => "login", :back_url => url }
138 format.atom { redirect_to :controller => "account", :action => "login", :back_url => url }
139 format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
139 format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
140 format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
140 format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
141 format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
141 format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
142 end
142 end
143 return false
143 return false
144 end
144 end
145 true
145 true
146 end
146 end
147
147
148 def require_admin
148 def require_admin
149 return unless require_login
149 return unless require_login
150 if !User.current.admin?
150 if !User.current.admin?
151 render_403
151 render_403
152 return false
152 return false
153 end
153 end
154 true
154 true
155 end
155 end
156
156
157 def deny_access
157 def deny_access
158 User.current.logged? ? render_403 : require_login
158 User.current.logged? ? render_403 : require_login
159 end
159 end
160
160
161 # Authorize the user for the requested action
161 # Authorize the user for the requested action
162 def authorize(ctrl = params[:controller], action = params[:action], global = false)
162 def authorize(ctrl = params[:controller], action = params[:action], global = false)
163 allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project || @projects, :global => global)
163 allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project || @projects, :global => global)
164 if allowed
164 if allowed
165 true
165 true
166 else
166 else
167 if @project && @project.archived?
167 if @project && @project.archived?
168 render_403 :message => :notice_not_authorized_archived_project
168 render_403 :message => :notice_not_authorized_archived_project
169 else
169 else
170 deny_access
170 deny_access
171 end
171 end
172 end
172 end
173 end
173 end
174
174
175 # Authorize the user for the requested action outside a project
175 # Authorize the user for the requested action outside a project
176 def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
176 def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
177 authorize(ctrl, action, global)
177 authorize(ctrl, action, global)
178 end
178 end
179
179
180 # Find project of id params[:id]
180 # Find project of id params[:id]
181 def find_project
181 def find_project
182 @project = Project.find(params[:id])
182 @project = Project.find(params[:id])
183 rescue ActiveRecord::RecordNotFound
183 rescue ActiveRecord::RecordNotFound
184 render_404
184 render_404
185 end
185 end
186
186
187 # Find project of id params[:project_id]
187 # Find project of id params[:project_id]
188 def find_project_by_project_id
188 def find_project_by_project_id
189 @project = Project.find(params[:project_id])
189 @project = Project.find(params[:project_id])
190 rescue ActiveRecord::RecordNotFound
190 rescue ActiveRecord::RecordNotFound
191 render_404
191 render_404
192 end
192 end
193
193
194 # Find a project based on params[:project_id]
194 # Find a project based on params[:project_id]
195 # TODO: some subclasses override this, see about merging their logic
195 # TODO: some subclasses override this, see about merging their logic
196 def find_optional_project
196 def find_optional_project
197 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
197 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
198 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
198 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
199 allowed ? true : deny_access
199 allowed ? true : deny_access
200 rescue ActiveRecord::RecordNotFound
200 rescue ActiveRecord::RecordNotFound
201 render_404
201 render_404
202 end
202 end
203
203
204 # Finds and sets @project based on @object.project
204 # Finds and sets @project based on @object.project
205 def find_project_from_association
205 def find_project_from_association
206 render_404 unless @object.present?
206 render_404 unless @object.present?
207
207
208 @project = @object.project
208 @project = @object.project
209 rescue ActiveRecord::RecordNotFound
209 rescue ActiveRecord::RecordNotFound
210 render_404
210 render_404
211 end
211 end
212
212
213 def find_model_object
213 def find_model_object
214 model = self.class.read_inheritable_attribute('model_object')
214 model = self.class.read_inheritable_attribute('model_object')
215 if model
215 if model
216 @object = model.find(params[:id])
216 @object = model.find(params[:id])
217 self.instance_variable_set('@' + controller_name.singularize, @object) if @object
217 self.instance_variable_set('@' + controller_name.singularize, @object) if @object
218 end
218 end
219 rescue ActiveRecord::RecordNotFound
219 rescue ActiveRecord::RecordNotFound
220 render_404
220 render_404
221 end
221 end
222
222
223 def self.model_object(model)
223 def self.model_object(model)
224 write_inheritable_attribute('model_object', model)
224 write_inheritable_attribute('model_object', model)
225 end
225 end
226
226
227 # Filter for bulk issue operations
227 # Filter for bulk issue operations
228 def find_issues
228 def find_issues
229 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
229 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
230 raise ActiveRecord::RecordNotFound if @issues.empty?
230 raise ActiveRecord::RecordNotFound if @issues.empty?
231 if @issues.detect {|issue| !issue.visible?}
231 if @issues.detect {|issue| !issue.visible?}
232 deny_access
232 deny_access
233 return
233 return
234 end
234 end
235 @projects = @issues.collect(&:project).compact.uniq
235 @projects = @issues.collect(&:project).compact.uniq
236 @project = @projects.first if @projects.size == 1
236 @project = @projects.first if @projects.size == 1
237 rescue ActiveRecord::RecordNotFound
237 rescue ActiveRecord::RecordNotFound
238 render_404
238 render_404
239 end
239 end
240
240
241 # Check if project is unique before bulk operations
241 # Check if project is unique before bulk operations
242 def check_project_uniqueness
242 def check_project_uniqueness
243 unless @project
243 unless @project
244 # TODO: let users bulk edit/move/destroy issues from different projects
244 # TODO: let users bulk edit/move/destroy issues from different projects
245 render_error 'Can not bulk edit/move/destroy issues from different projects'
245 render_error 'Can not bulk edit/move/destroy issues from different projects'
246 return false
246 return false
247 end
247 end
248 end
248 end
249
249
250 # make sure that the user is a member of the project (or admin) if project is private
250 # make sure that the user is a member of the project (or admin) if project is private
251 # used as a before_filter for actions that do not require any particular permission on the project
251 # used as a before_filter for actions that do not require any particular permission on the project
252 def check_project_privacy
252 def check_project_privacy
253 if @project && @project.active?
253 if @project && @project.active?
254 if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
254 if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
255 true
255 true
256 else
256 else
257 User.current.logged? ? render_403 : require_login
257 User.current.logged? ? render_403 : require_login
258 end
258 end
259 else
259 else
260 @project = nil
260 @project = nil
261 render_404
261 render_404
262 false
262 false
263 end
263 end
264 end
264 end
265
265
266 def back_url
266 def back_url
267 params[:back_url] || request.env['HTTP_REFERER']
267 params[:back_url] || request.env['HTTP_REFERER']
268 end
268 end
269
269
270 def redirect_back_or_default(default)
270 def redirect_back_or_default(default)
271 back_url = CGI.unescape(params[:back_url].to_s)
271 back_url = CGI.unescape(params[:back_url].to_s)
272 if !back_url.blank?
272 if !back_url.blank?
273 begin
273 begin
274 uri = URI.parse(back_url)
274 uri = URI.parse(back_url)
275 # do not redirect user to another host or to the login or register page
275 # do not redirect user to another host or to the login or register page
276 if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
276 if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
277 redirect_to(back_url)
277 redirect_to(back_url)
278 return
278 return
279 end
279 end
280 rescue URI::InvalidURIError
280 rescue URI::InvalidURIError
281 # redirect to default
281 # redirect to default
282 end
282 end
283 end
283 end
284 redirect_to default
284 redirect_to default
285 false
285 false
286 end
286 end
287
287
288 def render_403(options={})
288 def render_403(options={})
289 @project = nil
289 @project = nil
290 render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
290 render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
291 return false
291 return false
292 end
292 end
293
293
294 def render_404(options={})
294 def render_404(options={})
295 render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
295 render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
296 return false
296 return false
297 end
297 end
298
298
299 # Renders an error response
299 # Renders an error response
300 def render_error(arg)
300 def render_error(arg)
301 arg = {:message => arg} unless arg.is_a?(Hash)
301 arg = {:message => arg} unless arg.is_a?(Hash)
302
302
303 @message = arg[:message]
303 @message = arg[:message]
304 @message = l(@message) if @message.is_a?(Symbol)
304 @message = l(@message) if @message.is_a?(Symbol)
305 @status = arg[:status] || 500
305 @status = arg[:status] || 500
306
306
307 respond_to do |format|
307 respond_to do |format|
308 format.html {
308 format.html {
309 render :template => 'common/error', :layout => use_layout, :status => @status
309 render :template => 'common/error', :layout => use_layout, :status => @status
310 }
310 }
311 format.atom { head @status }
311 format.atom { head @status }
312 format.xml { head @status }
312 format.xml { head @status }
313 format.js { head @status }
313 format.js { head @status }
314 format.json { head @status }
314 format.json { head @status }
315 end
315 end
316 end
316 end
317
317
318 # Picks which layout to use based on the request
318 # Picks which layout to use based on the request
319 #
319 #
320 # @return [boolean, string] name of the layout to use or false for no layout
320 # @return [boolean, string] name of the layout to use or false for no layout
321 def use_layout
321 def use_layout
322 request.xhr? ? false : 'base'
322 request.xhr? ? false : 'base'
323 end
323 end
324
324
325 def invalid_authenticity_token
325 def invalid_authenticity_token
326 if api_request?
326 if api_request?
327 logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
327 logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
328 end
328 end
329 render_error "Invalid form authenticity token."
329 render_error "Invalid form authenticity token."
330 end
330 end
331
331
332 def render_feed(items, options={})
332 def render_feed(items, options={})
333 @items = items || []
333 @items = items || []
334 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
334 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
335 @items = @items.slice(0, Setting.feeds_limit.to_i)
335 @items = @items.slice(0, Setting.feeds_limit.to_i)
336 @title = options[:title] || Setting.app_title
336 @title = options[:title] || Setting.app_title
337 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
337 render :template => "common/feed.atom", :layout => false,
338 :content_type => 'application/atom+xml'
338 end
339 end
339
340
340 # TODO: remove in Redmine 1.4
341 # TODO: remove in Redmine 1.4
341 def self.accept_key_auth(*actions)
342 def self.accept_key_auth(*actions)
342 ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
343 ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
343 accept_rss_auth(*actions)
344 accept_rss_auth(*actions)
344 end
345 end
345
346
346 # TODO: remove in Redmine 1.4
347 # TODO: remove in Redmine 1.4
347 def accept_key_auth_actions
348 def accept_key_auth_actions
348 ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth_actions is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
349 ActiveSupport::Deprecation.warn "ApplicationController.accept_key_auth_actions is deprecated and will be removed in Redmine 1.4. Use accept_rss_auth (or accept_api_auth) instead."
349 self.class.accept_rss_auth
350 self.class.accept_rss_auth
350 end
351 end
351
352
352 def self.accept_rss_auth(*actions)
353 def self.accept_rss_auth(*actions)
353 if actions.any?
354 if actions.any?
354 write_inheritable_attribute('accept_rss_auth_actions', actions)
355 write_inheritable_attribute('accept_rss_auth_actions', actions)
355 else
356 else
356 read_inheritable_attribute('accept_rss_auth_actions') || []
357 read_inheritable_attribute('accept_rss_auth_actions') || []
357 end
358 end
358 end
359 end
359
360
360 def accept_rss_auth?(action=action_name)
361 def accept_rss_auth?(action=action_name)
361 self.class.accept_rss_auth.include?(action.to_sym)
362 self.class.accept_rss_auth.include?(action.to_sym)
362 end
363 end
363
364
364 def self.accept_api_auth(*actions)
365 def self.accept_api_auth(*actions)
365 if actions.any?
366 if actions.any?
366 write_inheritable_attribute('accept_api_auth_actions', actions)
367 write_inheritable_attribute('accept_api_auth_actions', actions)
367 else
368 else
368 read_inheritable_attribute('accept_api_auth_actions') || []
369 read_inheritable_attribute('accept_api_auth_actions') || []
369 end
370 end
370 end
371 end
371
372
372 def accept_api_auth?(action=action_name)
373 def accept_api_auth?(action=action_name)
373 self.class.accept_api_auth.include?(action.to_sym)
374 self.class.accept_api_auth.include?(action.to_sym)
374 end
375 end
375
376
376 # Returns the number of objects that should be displayed
377 # Returns the number of objects that should be displayed
377 # on the paginated list
378 # on the paginated list
378 def per_page_option
379 def per_page_option
379 per_page = nil
380 per_page = nil
380 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
381 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
381 per_page = params[:per_page].to_s.to_i
382 per_page = params[:per_page].to_s.to_i
382 session[:per_page] = per_page
383 session[:per_page] = per_page
383 elsif session[:per_page]
384 elsif session[:per_page]
384 per_page = session[:per_page]
385 per_page = session[:per_page]
385 else
386 else
386 per_page = Setting.per_page_options_array.first || 25
387 per_page = Setting.per_page_options_array.first || 25
387 end
388 end
388 per_page
389 per_page
389 end
390 end
390
391
391 # Returns offset and limit used to retrieve objects
392 # Returns offset and limit used to retrieve objects
392 # for an API response based on offset, limit and page parameters
393 # for an API response based on offset, limit and page parameters
393 def api_offset_and_limit(options=params)
394 def api_offset_and_limit(options=params)
394 if options[:offset].present?
395 if options[:offset].present?
395 offset = options[:offset].to_i
396 offset = options[:offset].to_i
396 if offset < 0
397 if offset < 0
397 offset = 0
398 offset = 0
398 end
399 end
399 end
400 end
400 limit = options[:limit].to_i
401 limit = options[:limit].to_i
401 if limit < 1
402 if limit < 1
402 limit = 25
403 limit = 25
403 elsif limit > 100
404 elsif limit > 100
404 limit = 100
405 limit = 100
405 end
406 end
406 if offset.nil? && options[:page].present?
407 if offset.nil? && options[:page].present?
407 offset = (options[:page].to_i - 1) * limit
408 offset = (options[:page].to_i - 1) * limit
408 offset = 0 if offset < 0
409 offset = 0 if offset < 0
409 end
410 end
410 offset ||= 0
411 offset ||= 0
411
412
412 [offset, limit]
413 [offset, limit]
413 end
414 end
414
415
415 # qvalues http header parser
416 # qvalues http header parser
416 # code taken from webrick
417 # code taken from webrick
417 def parse_qvalues(value)
418 def parse_qvalues(value)
418 tmp = []
419 tmp = []
419 if value
420 if value
420 parts = value.split(/,\s*/)
421 parts = value.split(/,\s*/)
421 parts.each {|part|
422 parts.each {|part|
422 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
423 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
423 val = m[1]
424 val = m[1]
424 q = (m[2] or 1).to_f
425 q = (m[2] or 1).to_f
425 tmp.push([val, q])
426 tmp.push([val, q])
426 end
427 end
427 }
428 }
428 tmp = tmp.sort_by{|val, q| -q}
429 tmp = tmp.sort_by{|val, q| -q}
429 tmp.collect!{|val, q| val}
430 tmp.collect!{|val, q| val}
430 end
431 end
431 return tmp
432 return tmp
432 rescue
433 rescue
433 nil
434 nil
434 end
435 end
435
436
436 # Returns a string that can be used as filename value in Content-Disposition header
437 # Returns a string that can be used as filename value in Content-Disposition header
437 def filename_for_content_disposition(name)
438 def filename_for_content_disposition(name)
438 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
439 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
439 end
440 end
440
441
441 def api_request?
442 def api_request?
442 %w(xml json).include? params[:format]
443 %w(xml json).include? params[:format]
443 end
444 end
444
445
445 # Returns the API key present in the request
446 # Returns the API key present in the request
446 def api_key_from_request
447 def api_key_from_request
447 if params[:key].present?
448 if params[:key].present?
448 params[:key]
449 params[:key]
449 elsif request.headers["X-Redmine-API-Key"].present?
450 elsif request.headers["X-Redmine-API-Key"].present?
450 request.headers["X-Redmine-API-Key"]
451 request.headers["X-Redmine-API-Key"]
451 end
452 end
452 end
453 end
453
454
454 # Renders a warning flash if obj has unsaved attachments
455 # Renders a warning flash if obj has unsaved attachments
455 def render_attachment_warning_if_needed(obj)
456 def render_attachment_warning_if_needed(obj)
456 flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
457 flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
457 end
458 end
458
459
459 # Sets the `flash` notice or error based the number of issues that did not save
460 # Sets the `flash` notice or error based the number of issues that did not save
460 #
461 #
461 # @param [Array, Issue] issues all of the saved and unsaved Issues
462 # @param [Array, Issue] issues all of the saved and unsaved Issues
462 # @param [Array, Integer] unsaved_issue_ids the issue ids that were not saved
463 # @param [Array, Integer] unsaved_issue_ids the issue ids that were not saved
463 def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
464 def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
464 if unsaved_issue_ids.empty?
465 if unsaved_issue_ids.empty?
465 flash[:notice] = l(:notice_successful_update) unless issues.empty?
466 flash[:notice] = l(:notice_successful_update) unless issues.empty?
466 else
467 else
467 flash[:error] = l(:notice_failed_to_save_issues,
468 flash[:error] = l(:notice_failed_to_save_issues,
468 :count => unsaved_issue_ids.size,
469 :count => unsaved_issue_ids.size,
469 :total => issues.size,
470 :total => issues.size,
470 :ids => '#' + unsaved_issue_ids.join(', #'))
471 :ids => '#' + unsaved_issue_ids.join(', #'))
471 end
472 end
472 end
473 end
473
474
474 # Rescues an invalid query statement. Just in case...
475 # Rescues an invalid query statement. Just in case...
475 def query_statement_invalid(exception)
476 def query_statement_invalid(exception)
476 logger.error "Query::StatementInvalid: #{exception.message}" if logger
477 logger.error "Query::StatementInvalid: #{exception.message}" if logger
477 session.delete(:query)
478 session.delete(:query)
478 sort_clear if respond_to?(:sort_clear)
479 sort_clear if respond_to?(:sort_clear)
479 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
480 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
480 end
481 end
481
482
482 # Converts the errors on an ActiveRecord object into a common JSON format
483 # Converts the errors on an ActiveRecord object into a common JSON format
483 def object_errors_to_json(object)
484 def object_errors_to_json(object)
484 object.errors.collect do |attribute, error|
485 object.errors.collect do |attribute, error|
485 { attribute => error }
486 { attribute => error }
486 end.to_json
487 end.to_json
487 end
488 end
488
489
489 # Renders API response on validation failure
490 # Renders API response on validation failure
490 def render_validation_errors(object)
491 def render_validation_errors(object)
491 options = { :status => :unprocessable_entity, :layout => false }
492 options = { :status => :unprocessable_entity, :layout => false }
492 options.merge!(case params[:format]
493 options.merge!(case params[:format]
493 when 'xml'; { :xml => object.errors }
494 when 'xml'; { :xml => object.errors }
494 when 'json'; { :json => {'errors' => object.errors} } # ActiveResource client compliance
495 when 'json'; { :json => {'errors' => object.errors} } # ActiveResource client compliance
495 else
496 else
496 raise "Unknown format #{params[:format]} in #render_validation_errors"
497 raise "Unknown format #{params[:format]} in #render_validation_errors"
497 end
498 end
498 )
499 )
499 render options
500 render options
500 end
501 end
501
502
502 # Overrides #default_template so that the api template
503 # Overrides #default_template so that the api template
503 # is used automatically if it exists
504 # is used automatically if it exists
504 def default_template(action_name = self.action_name)
505 def default_template(action_name = self.action_name)
505 if api_request?
506 if api_request?
506 begin
507 begin
507 return self.view_paths.find_template(default_template_name(action_name), 'api')
508 return self.view_paths.find_template(default_template_name(action_name), 'api')
508 rescue ::ActionView::MissingTemplate
509 rescue ::ActionView::MissingTemplate
509 # the api template was not found
510 # the api template was not found
510 # fallback to the default behaviour
511 # fallback to the default behaviour
511 end
512 end
512 end
513 end
513 super
514 super
514 end
515 end
515
516
516 # Overrides #pick_layout so that #render with no arguments
517 # Overrides #pick_layout so that #render with no arguments
517 # doesn't use the layout for api requests
518 # doesn't use the layout for api requests
518 def pick_layout(*args)
519 def pick_layout(*args)
519 api_request? ? nil : super
520 api_request? ? nil : super
520 end
521 end
521 end
522 end
General Comments 0
You need to be logged in to leave comments. Login now