comparison app/controllers/application_controller.rb @ 442:753f1380d6bc cannam

Merge from branch "redmine-1.2"
author Chris Cannam
date Mon, 06 Jun 2011 14:41:04 +0100
parents 73ff0e6a11b1 cbce1fd3b1b7
children 350acce374a2
comparison
equal deleted inserted replaced
252:adc8466df404 442:753f1380d6bc
1 # redMine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2007 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'
21 class ApplicationController < ActionController::Base 21 class ApplicationController < ActionController::Base
22 include Redmine::I18n 22 include Redmine::I18n
23 23
24 layout 'base' 24 layout 'base'
25 exempt_from_layout 'builder', 'rsb' 25 exempt_from_layout 'builder', 'rsb'
26 26
27 # Remove broken cookie after upgrade from 0.8.x (#4292) 27 # Remove broken cookie after upgrade from 0.8.x (#4292)
28 # See https://rails.lighthouseapp.com/projects/8994/tickets/3360 28 # See https://rails.lighthouseapp.com/projects/8994/tickets/3360
29 # TODO: remove it when Rails is fixed 29 # TODO: remove it when Rails is fixed
30 before_filter :delete_broken_cookies 30 before_filter :delete_broken_cookies
31 def delete_broken_cookies 31 def delete_broken_cookies
32 if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/ 32 if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
33 cookies.delete '_redmine_session' 33 cookies.delete '_redmine_session'
34 redirect_to home_path 34 redirect_to home_path
35 return false 35 return false
36 end 36 end
37 end 37 end
38 38
39 before_filter :user_setup, :check_if_login_required, :set_localization 39 before_filter :user_setup, :check_if_login_required, :set_localization
40 filter_parameter_logging :password 40 filter_parameter_logging :password
41 protect_from_forgery 41 protect_from_forgery
42 42
43 rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token 43 rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
44 44
45 include Redmine::Search::Controller 45 include Redmine::Search::Controller
46 include Redmine::MenuManager::MenuController 46 include Redmine::MenuManager::MenuController
47 helper Redmine::MenuManager::MenuHelper 47 helper Redmine::MenuManager::MenuHelper
48 48
49 Redmine::Scm::Base.all.each do |scm| 49 Redmine::Scm::Base.all.each do |scm|
50 require_dependency "repository/#{scm.underscore}" 50 require_dependency "repository/#{scm.underscore}"
51 end 51 end
52 52
53 def user_setup 53 def user_setup
54 # Check the settings cache for each request 54 # Check the settings cache for each request
55 Setting.check_cache 55 Setting.check_cache
56 # Find the current user 56 # Find the current user
57 User.current = find_current_user 57 User.current = find_current_user
58 end 58 end
59 59
60 # Returns the current user or nil if no user is logged in 60 # Returns the current user or nil if no user is logged in
61 # and starts a session if needed 61 # and starts a session if needed
62 def find_current_user 62 def find_current_user
63 if session[:user_id] 63 if session[:user_id]
64 # existing session 64 # existing session
92 session[:user_id] = user.id 92 session[:user_id] = user.id
93 else 93 else
94 User.current = User.anonymous 94 User.current = User.anonymous
95 end 95 end
96 end 96 end
97 97
98 # check if login is globally required to access the application 98 # check if login is globally required to access the application
99 def check_if_login_required 99 def check_if_login_required
100 # no check needed if user is already logged in 100 # no check needed if user is already logged in
101 return true if User.current.logged? 101 return true if User.current.logged?
102 require_login if Setting.login_required? 102 require_login if Setting.login_required?
103 end 103 end
104 104
105 def set_localization 105 def set_localization
106 lang = nil 106 lang = nil
107 if User.current.logged? 107 if User.current.logged?
108 lang = find_language(User.current.language) 108 lang = find_language(User.current.language)
109 end 109 end
115 end 115 end
116 end 116 end
117 lang ||= Setting.default_language 117 lang ||= Setting.default_language
118 set_language_if_valid(lang) 118 set_language_if_valid(lang)
119 end 119 end
120 120
121 def require_login 121 def require_login
122 if !User.current.logged? 122 if !User.current.logged?
123 # Extract only the basic url parameters on non-GET requests 123 # Extract only the basic url parameters on non-GET requests
124 if request.get? 124 if request.get?
125 url = url_for(params) 125 url = url_for(params)
144 render_403 144 render_403
145 return false 145 return false
146 end 146 end
147 true 147 true
148 end 148 end
149 149
150 def deny_access 150 def deny_access
151 User.current.logged? ? render_403 : require_login 151 User.current.logged? ? render_403 : require_login
152 end 152 end
153 153
154 # Authorize the user for the requested action 154 # Authorize the user for the requested action
195 end 195 end
196 196
197 # Finds and sets @project based on @object.project 197 # Finds and sets @project based on @object.project
198 def find_project_from_association 198 def find_project_from_association
199 render_404 unless @object.present? 199 render_404 unless @object.present?
200 200
201 @project = @object.project 201 @project = @object.project
202 rescue ActiveRecord::RecordNotFound 202 rescue ActiveRecord::RecordNotFound
203 render_404 203 render_404
204 end 204 end
205 205
219 219
220 # Filter for bulk issue operations 220 # Filter for bulk issue operations
221 def find_issues 221 def find_issues
222 @issues = Issue.find_all_by_id(params[:id] || params[:ids]) 222 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
223 raise ActiveRecord::RecordNotFound if @issues.empty? 223 raise ActiveRecord::RecordNotFound if @issues.empty?
224 if @issues.detect {|issue| !issue.visible?}
225 deny_access
226 return
227 end
224 @projects = @issues.collect(&:project).compact.uniq 228 @projects = @issues.collect(&:project).compact.uniq
225 @project = @projects.first if @projects.size == 1 229 @project = @projects.first if @projects.size == 1
226 rescue ActiveRecord::RecordNotFound 230 rescue ActiveRecord::RecordNotFound
227 render_404 231 render_404
228 end 232 end
229 233
230 # Check if project is unique before bulk operations 234 # Check if project is unique before bulk operations
231 def check_project_uniqueness 235 def check_project_uniqueness
232 unless @project 236 unless @project
233 # TODO: let users bulk edit/move/destroy issues from different projects 237 # TODO: let users bulk edit/move/destroy issues from different projects
234 render_error 'Can not bulk edit/move/destroy issues from different projects' 238 render_error 'Can not bulk edit/move/destroy issues from different projects'
235 return false 239 return false
236 end 240 end
237 end 241 end
238 242
239 # make sure that the user is a member of the project (or admin) if project is private 243 # make sure that the user is a member of the project (or admin) if project is private
240 # used as a before_filter for actions that do not require any particular permission on the project 244 # used as a before_filter for actions that do not require any particular permission on the project
241 def check_project_privacy 245 def check_project_privacy
242 if @project && @project.active? 246 if @project && @project.active?
243 if @project.is_public? || User.current.member_of?(@project) || User.current.admin? 247 if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
275 rescue URI::InvalidURIError 279 rescue URI::InvalidURIError
276 # redirect to default 280 # redirect to default
277 end 281 end
278 end 282 end
279 redirect_to default 283 redirect_to default
280 end 284 false
281 285 end
286
282 def render_403(options={}) 287 def render_403(options={})
283 @project = nil 288 @project = nil
284 render_error({:message => :notice_not_authorized, :status => 403}.merge(options)) 289 render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
285 return false 290 return false
286 end 291 end
287 292
288 def render_404(options={}) 293 def render_404(options={})
289 render_error({:message => :notice_file_not_found, :status => 404}.merge(options)) 294 render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
290 return false 295 return false
291 end 296 end
292 297
293 # Renders an error response 298 # Renders an error response
294 def render_error(arg) 299 def render_error(arg)
295 arg = {:message => arg} unless arg.is_a?(Hash) 300 arg = {:message => arg} unless arg.is_a?(Hash)
296 301
297 @message = arg[:message] 302 @message = arg[:message]
298 @message = l(@message) if @message.is_a?(Symbol) 303 @message = l(@message) if @message.is_a?(Symbol)
299 @status = arg[:status] || 500 304 @status = arg[:status] || 500
300 305
301 respond_to do |format| 306 respond_to do |format|
302 format.html { 307 format.html {
303 render :template => 'common/error', :layout => use_layout, :status => @status 308 render :template => 'common/error', :layout => use_layout, :status => @status
304 } 309 }
305 format.atom { head @status } 310 format.atom { head @status }
313 # 318 #
314 # @return [boolean, string] name of the layout to use or false for no layout 319 # @return [boolean, string] name of the layout to use or false for no layout
315 def use_layout 320 def use_layout
316 request.xhr? ? false : 'base' 321 request.xhr? ? false : 'base'
317 end 322 end
318 323
319 def invalid_authenticity_token 324 def invalid_authenticity_token
320 if api_request? 325 if api_request?
321 logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)." 326 logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
322 end 327 end
323 render_error "Invalid form authenticity token. Perhaps your session has timed out; try reloading the form and entering your details again." 328 render_error "Invalid form authenticity token. Perhaps your session has timed out; try reloading the form and entering your details again."
324 end 329 end
325 330
326 def render_feed(items, options={}) 331 def render_feed(items, options={})
327 @items = items || [] 332 @items = items || []
328 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime } 333 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
329 @items = @items.slice(0, Setting.feeds_limit.to_i) 334 @items = @items.slice(0, Setting.feeds_limit.to_i)
330 @title = options[:title] || Setting.app_title 335 @title = options[:title] || Setting.app_title
331 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml' 336 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
332 end 337 end
333 338
334 def self.accept_key_auth(*actions) 339 def self.accept_key_auth(*actions)
335 actions = actions.flatten.map(&:to_s) 340 actions = actions.flatten.map(&:to_s)
336 write_inheritable_attribute('accept_key_auth_actions', actions) 341 write_inheritable_attribute('accept_key_auth_actions', actions)
337 end 342 end
338 343
339 def accept_key_auth_actions 344 def accept_key_auth_actions
340 self.class.read_inheritable_attribute('accept_key_auth_actions') || [] 345 self.class.read_inheritable_attribute('accept_key_auth_actions') || []
341 end 346 end
342 347
343 # Returns the number of objects that should be displayed 348 # Returns the number of objects that should be displayed
344 # on the paginated list 349 # on the paginated list
345 def per_page_option 350 def per_page_option
346 per_page = nil 351 per_page = nil
347 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i) 352 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
373 if offset.nil? && options[:page].present? 378 if offset.nil? && options[:page].present?
374 offset = (options[:page].to_i - 1) * limit 379 offset = (options[:page].to_i - 1) * limit
375 offset = 0 if offset < 0 380 offset = 0 if offset < 0
376 end 381 end
377 offset ||= 0 382 offset ||= 0
378 383
379 [offset, limit] 384 [offset, limit]
380 end 385 end
381 386
382 # qvalues http header parser 387 # qvalues http header parser
383 # code taken from webrick 388 # code taken from webrick
384 def parse_qvalues(value) 389 def parse_qvalues(value)
385 tmp = [] 390 tmp = []
386 if value 391 if value
397 end 402 end
398 return tmp 403 return tmp
399 rescue 404 rescue
400 nil 405 nil
401 end 406 end
402 407
403 # Returns a string that can be used as filename value in Content-Disposition header 408 # Returns a string that can be used as filename value in Content-Disposition header
404 def filename_for_content_disposition(name) 409 def filename_for_content_disposition(name)
405 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name 410 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
406 end 411 end
407 412
408 def api_request? 413 def api_request?
409 %w(xml json).include? params[:format] 414 %w(xml json).include? params[:format]
410 end 415 end
411 416
412 # Returns the API key present in the request 417 # Returns the API key present in the request
413 def api_key_from_request 418 def api_key_from_request
414 if params[:key].present? 419 if params[:key].present?
415 params[:key] 420 params[:key]
416 elsif request.headers["X-Redmine-API-Key"].present? 421 elsif request.headers["X-Redmine-API-Key"].present?
463 raise "Unknown format #{params[:format]} in #render_validation_errors" 468 raise "Unknown format #{params[:format]} in #render_validation_errors"
464 end 469 end
465 ) 470 )
466 render options 471 render options
467 end 472 end
468 473
469 # Overrides #default_template so that the api template 474 # Overrides #default_template so that the api template
470 # is used automatically if it exists 475 # is used automatically if it exists
471 def default_template(action_name = self.action_name) 476 def default_template(action_name = self.action_name)
472 if api_request? 477 if api_request?
473 begin 478 begin
477 # fallback to the default behaviour 482 # fallback to the default behaviour
478 end 483 end
479 end 484 end
480 super 485 super
481 end 486 end
482 487
483 # Overrides #pick_layout so that #render with no arguments 488 # Overrides #pick_layout so that #render with no arguments
484 # doesn't use the layout for api requests 489 # doesn't use the layout for api requests
485 def pick_layout(*args) 490 def pick_layout(*args)
486 api_request? ? nil : super 491 api_request? ? nil : super
487 end 492 end