OSDN Git Service

add Redmine trunk rev 3089
[redminele/redminele.git] / redmine / app / controllers / application_controller.rb
1 # redMine - project management software
2 # Copyright (C) 2006-2007  Jean-Philippe Lang
3 #
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.
8
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.
13
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.
17
18 require 'uri'
19 require 'cgi'
20
21 class ApplicationController < ActionController::Base
22   include Redmine::I18n
23
24   layout 'base'
25   
26   before_filter :user_setup, :check_if_login_required, :set_localization
27   filter_parameter_logging :password
28   protect_from_forgery
29   
30   include Redmine::Search::Controller
31   include Redmine::MenuManager::MenuController
32   helper Redmine::MenuManager::MenuHelper
33   
34   REDMINE_SUPPORTED_SCM.each do |scm|
35     require_dependency "repository/#{scm.underscore}"
36   end
37   
38   def user_setup
39     # Check the settings cache for each request
40     Setting.check_cache
41     # Find the current user
42     User.current = find_current_user
43   end
44   
45   # Returns the current user or nil if no user is logged in
46   # and starts a session if needed
47   def find_current_user
48     if session[:user_id]
49       # existing session
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
55       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])
59     end
60   end
61   
62   # Sets the logged in user
63   def logged_user=(user)
64     reset_session
65     if user && user.is_a?(User)
66       User.current = user
67       session[:user_id] = user.id
68     else
69       User.current = User.anonymous
70     end
71   end
72   
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?
78   end 
79   
80   def set_localization
81     lang = nil
82     if User.current.logged?
83       lang = find_language(User.current.language)
84     end
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)
89       end
90     end
91     lang ||= Setting.default_language
92     set_language_if_valid(lang)
93   end
94   
95   def require_login
96     if !User.current.logged?
97       # Extract only the basic url parameters on non-GET requests
98       if request.get?
99         url = url_for(params)
100       else
101         url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
102       end
103       redirect_to :controller => "account", :action => "login", :back_url => url
104       return false
105     end
106     true
107   end
108
109   def require_admin
110     return unless require_login
111     if !User.current.admin?
112       render_403
113       return false
114     end
115     true
116   end
117   
118   def deny_access
119     User.current.logged? ? render_403 : require_login
120   end
121
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
126   end
127
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)
131   end
132   
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?
138         true
139       else
140         User.current.logged? ? render_403 : require_login
141       end
142     else
143       @project = nil
144       render_404
145       false
146     end
147   end
148
149   def redirect_back_or_default(default)
150     back_url = CGI.unescape(params[:back_url].to_s)
151     if !back_url.blank?
152       begin
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
157         end
158       rescue URI::InvalidURIError
159         # redirect to default
160       end
161     end
162     redirect_to default
163   end
164   
165   def render_403
166     @project = nil
167     render :template => "common/403", :layout => !request.xhr?, :status => 403
168     return false
169   end
170     
171   def render_404
172     render :template => "common/404", :layout => !request.xhr?, :status => 404
173     return false
174   end
175   
176   def render_error(msg)
177     flash.now[:error] = msg
178     render :text => '', :layout => !request.xhr?, :status => 500
179   end
180   
181   def render_feed(items, options={})    
182     @items = items || []
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'
187   end
188   
189   def self.accept_key_auth(*actions)
190     actions = actions.flatten.map(&:to_s)
191     write_inheritable_attribute('accept_key_auth_actions', actions)
192   end
193   
194   def accept_key_auth_actions
195     self.class.read_inheritable_attribute('accept_key_auth_actions') || []
196   end
197   
198   # TODO: move to model
199   def attach_files(obj, attachments)
200     attached = []
201     unsaved = []
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, 
207                               :file => file,
208                               :description => attachment['description'].to_s.strip,
209                               :author => User.current)
210         a.new_record? ? (unsaved << a) : (attached << a)
211       end
212       if unsaved.any?
213         flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
214       end
215     end
216     attached
217   end
218
219   # Returns the number of objects that should be displayed
220   # on the paginated list
221   def per_page_option
222     per_page = nil
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]
228     else
229       per_page = Setting.per_page_options_array.first || 25
230     end
231     per_page
232   end
233
234   # qvalues http header parser
235   # code taken from webrick
236   def parse_qvalues(value)
237     tmp = []
238     if value
239       parts = value.split(/,\s*/)
240       parts.each {|part|
241         if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
242           val = m[1]
243           q = (m[2] or 1).to_f
244           tmp.push([val, q])
245         end
246       }
247       tmp = tmp.sort_by{|val, q| -q}
248       tmp.collect!{|val, q| val}
249     end
250     return tmp
251   rescue
252     nil
253   end
254   
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
258   end
259 end