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