1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
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
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 class ApplicationController < ActionController::Base
26 before_filter :user_setup, :check_if_login_required, :set_localization
27 filter_parameter_logging :password
30 include Redmine::Search::Controller
31 include Redmine::MenuManager::MenuController
32 helper Redmine::MenuManager::MenuHelper
34 REDMINE_SUPPORTED_SCM.each do |scm|
35 require_dependency "repository/#{scm.underscore}"
39 # Check the settings cache for each request
41 # Find the current user
42 User.current = find_current_user
45 # Returns the current user or nil if no user is logged in
46 # and starts a session if needed
50 (User.active.find(session[:user_id]) rescue nil)
51 elsif cookies[:autologin] && Setting.autologin?
52 # auto-login feature starts a new session
53 user = User.try_to_autologin(cookies[:autologin])
54 session[:user_id] = user.id if user
56 elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
57 # RSS key authentication does not start a session
58 User.find_by_rss_key(params[:key])
62 # Sets the logged in user
63 def logged_user=(user)
65 if user && user.is_a?(User)
67 session[:user_id] = user.id
69 User.current = User.anonymous
73 # check if login is globally required to access the application
74 def check_if_login_required
75 # no check needed if user is already logged in
76 return true if User.current.logged?
77 require_login if Setting.login_required?
82 if User.current.logged?
83 lang = find_language(User.current.language)
85 if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
86 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.downcase
87 if !accept_lang.blank?
88 lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
91 lang ||= Setting.default_language
92 set_language_if_valid(lang)
96 if !User.current.logged?
97 # Extract only the basic url parameters on non-GET requests
101 url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
103 redirect_to :controller => "account", :action => "login", :back_url => url
110 return unless require_login
111 if !User.current.admin?
119 User.current.logged? ? render_403 : require_login
122 # Authorize the user for the requested action
123 def authorize(ctrl = params[:controller], action = params[:action], global = false)
124 allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project, :global => global)
125 allowed ? true : deny_access
128 # Authorize the user for the requested action outside a project
129 def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
130 authorize(ctrl, action, global)
133 # make sure that the user is a member of the project (or admin) if project is private
134 # used as a before_filter for actions that do not require any particular permission on the project
135 def check_project_privacy
136 if @project && @project.active?
137 if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
140 User.current.logged? ? render_403 : require_login
149 def redirect_back_or_default(default)
150 back_url = CGI.unescape(params[:back_url].to_s)
153 uri = URI.parse(back_url)
154 # do not redirect user to another host or to the login or register page
155 if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
156 redirect_to(back_url) and return
158 rescue URI::InvalidURIError
159 # redirect to default
167 render :template => "common/403", :layout => !request.xhr?, :status => 403
172 render :template => "common/404", :layout => !request.xhr?, :status => 404
176 def render_error(msg)
177 flash.now[:error] = msg
178 render :text => '', :layout => !request.xhr?, :status => 500
181 def render_feed(items, options={})
183 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
184 @items = @items.slice(0, Setting.feeds_limit.to_i)
185 @title = options[:title] || Setting.app_title
186 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
189 def self.accept_key_auth(*actions)
190 actions = actions.flatten.map(&:to_s)
191 write_inheritable_attribute('accept_key_auth_actions', actions)
194 def accept_key_auth_actions
195 self.class.read_inheritable_attribute('accept_key_auth_actions') || []
198 # TODO: move to model
199 def attach_files(obj, attachments)
202 if attachments && attachments.is_a?(Hash)
203 attachments.each_value do |attachment|
204 file = attachment['file']
205 next unless file && file.size > 0
206 a = Attachment.create(:container => obj,
208 :description => attachment['description'].to_s.strip,
209 :author => User.current)
210 a.new_record? ? (unsaved << a) : (attached << a)
213 flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
219 # Returns the number of objects that should be displayed
220 # on the paginated list
223 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
224 per_page = params[:per_page].to_s.to_i
225 session[:per_page] = per_page
226 elsif session[:per_page]
227 per_page = session[:per_page]
229 per_page = Setting.per_page_options_array.first || 25
234 # qvalues http header parser
235 # code taken from webrick
236 def parse_qvalues(value)
239 parts = value.split(/,\s*/)
241 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
247 tmp = tmp.sort_by{|val, q| -q}
248 tmp.collect!{|val, q| val}
255 # Returns a string that can be used as filename value in Content-Disposition header
256 def filename_for_content_disposition(name)
257 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name