comparison app/controllers/application_controller.rb @ 441:cbce1fd3b1b7 redmine-1.2

Update to Redmine 1.2-stable branch (Redmine SVN rev 6000)
author Chris Cannam
date Mon, 06 Jun 2011 14:24:13 +0100
parents 8661b858af72
children 753f1380d6bc 0c939c159af4
comparison
equal deleted inserted replaced
245:051f544170fe 441:cbce1fd3b1b7
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?
269 rescue URI::InvalidURIError 273 rescue URI::InvalidURIError
270 # redirect to default 274 # redirect to default
271 end 275 end
272 end 276 end
273 redirect_to default 277 redirect_to default
274 end 278 false
275 279 end
280
276 def render_403(options={}) 281 def render_403(options={})
277 @project = nil 282 @project = nil
278 render_error({:message => :notice_not_authorized, :status => 403}.merge(options)) 283 render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
279 return false 284 return false
280 end 285 end
281 286
282 def render_404(options={}) 287 def render_404(options={})
283 render_error({:message => :notice_file_not_found, :status => 404}.merge(options)) 288 render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
284 return false 289 return false
285 end 290 end
286 291
287 # Renders an error response 292 # Renders an error response
288 def render_error(arg) 293 def render_error(arg)
289 arg = {:message => arg} unless arg.is_a?(Hash) 294 arg = {:message => arg} unless arg.is_a?(Hash)
290 295
291 @message = arg[:message] 296 @message = arg[:message]
292 @message = l(@message) if @message.is_a?(Symbol) 297 @message = l(@message) if @message.is_a?(Symbol)
293 @status = arg[:status] || 500 298 @status = arg[:status] || 500
294 299
295 respond_to do |format| 300 respond_to do |format|
296 format.html { 301 format.html {
297 render :template => 'common/error', :layout => use_layout, :status => @status 302 render :template => 'common/error', :layout => use_layout, :status => @status
298 } 303 }
299 format.atom { head @status } 304 format.atom { head @status }
307 # 312 #
308 # @return [boolean, string] name of the layout to use or false for no layout 313 # @return [boolean, string] name of the layout to use or false for no layout
309 def use_layout 314 def use_layout
310 request.xhr? ? false : 'base' 315 request.xhr? ? false : 'base'
311 end 316 end
312 317
313 def invalid_authenticity_token 318 def invalid_authenticity_token
314 if api_request? 319 if api_request?
315 logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)." 320 logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
316 end 321 end
317 render_error "Invalid form authenticity token." 322 render_error "Invalid form authenticity token."
318 end 323 end
319 324
320 def render_feed(items, options={}) 325 def render_feed(items, options={})
321 @items = items || [] 326 @items = items || []
322 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime } 327 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
323 @items = @items.slice(0, Setting.feeds_limit.to_i) 328 @items = @items.slice(0, Setting.feeds_limit.to_i)
324 @title = options[:title] || Setting.app_title 329 @title = options[:title] || Setting.app_title
325 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml' 330 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
326 end 331 end
327 332
328 def self.accept_key_auth(*actions) 333 def self.accept_key_auth(*actions)
329 actions = actions.flatten.map(&:to_s) 334 actions = actions.flatten.map(&:to_s)
330 write_inheritable_attribute('accept_key_auth_actions', actions) 335 write_inheritable_attribute('accept_key_auth_actions', actions)
331 end 336 end
332 337
333 def accept_key_auth_actions 338 def accept_key_auth_actions
334 self.class.read_inheritable_attribute('accept_key_auth_actions') || [] 339 self.class.read_inheritable_attribute('accept_key_auth_actions') || []
335 end 340 end
336 341
337 # Returns the number of objects that should be displayed 342 # Returns the number of objects that should be displayed
338 # on the paginated list 343 # on the paginated list
339 def per_page_option 344 def per_page_option
340 per_page = nil 345 per_page = nil
341 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i) 346 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
367 if offset.nil? && options[:page].present? 372 if offset.nil? && options[:page].present?
368 offset = (options[:page].to_i - 1) * limit 373 offset = (options[:page].to_i - 1) * limit
369 offset = 0 if offset < 0 374 offset = 0 if offset < 0
370 end 375 end
371 offset ||= 0 376 offset ||= 0
372 377
373 [offset, limit] 378 [offset, limit]
374 end 379 end
375 380
376 # qvalues http header parser 381 # qvalues http header parser
377 # code taken from webrick 382 # code taken from webrick
378 def parse_qvalues(value) 383 def parse_qvalues(value)
379 tmp = [] 384 tmp = []
380 if value 385 if value
391 end 396 end
392 return tmp 397 return tmp
393 rescue 398 rescue
394 nil 399 nil
395 end 400 end
396 401
397 # Returns a string that can be used as filename value in Content-Disposition header 402 # Returns a string that can be used as filename value in Content-Disposition header
398 def filename_for_content_disposition(name) 403 def filename_for_content_disposition(name)
399 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name 404 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
400 end 405 end
401 406
402 def api_request? 407 def api_request?
403 %w(xml json).include? params[:format] 408 %w(xml json).include? params[:format]
404 end 409 end
405 410
406 # Returns the API key present in the request 411 # Returns the API key present in the request
407 def api_key_from_request 412 def api_key_from_request
408 if params[:key].present? 413 if params[:key].present?
409 params[:key] 414 params[:key]
410 elsif request.headers["X-Redmine-API-Key"].present? 415 elsif request.headers["X-Redmine-API-Key"].present?
457 raise "Unknown format #{params[:format]} in #render_validation_errors" 462 raise "Unknown format #{params[:format]} in #render_validation_errors"
458 end 463 end
459 ) 464 )
460 render options 465 render options
461 end 466 end
462 467
463 # Overrides #default_template so that the api template 468 # Overrides #default_template so that the api template
464 # is used automatically if it exists 469 # is used automatically if it exists
465 def default_template(action_name = self.action_name) 470 def default_template(action_name = self.action_name)
466 if api_request? 471 if api_request?
467 begin 472 begin
471 # fallback to the default behaviour 476 # fallback to the default behaviour
472 end 477 end
473 end 478 end
474 super 479 super
475 end 480 end
476 481
477 # Overrides #pick_layout so that #render with no arguments 482 # Overrides #pick_layout so that #render with no arguments
478 # doesn't use the layout for api requests 483 # doesn't use the layout for api requests
479 def pick_layout(*args) 484 def pick_layout(*args)
480 api_request? ? nil : super 485 api_request? ? nil : super
481 end 486 end