Class: ApplicationController

Inherits:
ActionController::Base
  • Object
show all
Includes:
Redmine::I18n, Redmine::MenuManager::MenuController, Redmine::Search::Controller
Defined in:
app/controllers/application_controller.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Redmine::MenuManager::MenuController

#current_menu_item, included, #menu_items, #redirect_to_project_menu_item

Methods included from Redmine::Search::Controller

#default_search_scope, #default_search_scopes, included

Methods included from Redmine::I18n

#current_language, #day_name, #find_language, #format_date, #format_time, included, #l, #l_hours, #l_or_humanize, #ll, #month_name, #set_language_if_valid, #valid_languages

Class Method Details

.accept_key_auth(*actions) ⇒ Object



328
329
330
331
# File 'app/controllers/application_controller.rb', line 328

def self.accept_key_auth(*actions)
  actions = actions.flatten.map(&:to_s)
  write_inheritable_attribute('accept_key_auth_actions', actions)
end

.model_object(model) ⇒ Object



216
217
218
# File 'app/controllers/application_controller.rb', line 216

def self.model_object(model)
  write_inheritable_attribute('model_object', model)
end

Instance Method Details

#accept_key_auth_actionsObject



333
334
335
# File 'app/controllers/application_controller.rb', line 333

def accept_key_auth_actions
  self.class.read_inheritable_attribute('accept_key_auth_actions') || []
end

#api_key_from_requestObject

Returns the API key present in the request



407
408
409
410
411
412
413
# File 'app/controllers/application_controller.rb', line 407

def api_key_from_request
  if params[:key].present?
    params[:key]
  elsif request.headers["X-Redmine-API-Key"].present?
    request.headers["X-Redmine-API-Key"]
  end
end

#api_offset_and_limit(options = params) ⇒ Object

Returns offset and limit used to retrieve objects for an API response based on offset, limit and page parameters



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'app/controllers/application_controller.rb', line 354

def api_offset_and_limit(options=params)
  if options[:offset].present?
    offset = options[:offset].to_i
    if offset < 0
      offset = 0
    end
  end
  limit = options[:limit].to_i
  if limit < 1
    limit = 25
  elsif limit > 100
    limit = 100
  end
  if offset.nil? && options[:page].present?
    offset = (options[:page].to_i - 1) * limit
    offset = 0 if offset < 0
  end
  offset ||= 0
  
  [offset, limit]
end

#api_request?Boolean



402
403
404
# File 'app/controllers/application_controller.rb', line 402

def api_request?
  %w(xml json).include? params[:format]
end

#authorize(ctrl = params[:controller], action = params[:action], global = false) ⇒ Object

Authorize the user for the requested action



155
156
157
158
159
160
161
162
163
164
165
166
# File 'app/controllers/application_controller.rb', line 155

def authorize(ctrl = params[:controller], action = params[:action], global = false)
  allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project || @projects, :global => global)
  if allowed
    true
  else
    if @project && @project.archived?
      render_403 :message => :notice_not_authorized_archived_project
    else
      deny_access
    end
  end
end

#authorize_global(ctrl = params[:controller], action = params[:action], global = true) ⇒ Object

Authorize the user for the requested action outside a project



169
170
171
# File 'app/controllers/application_controller.rb', line 169

def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
  authorize(ctrl, action, global)
end

#back_urlObject



255
256
257
# File 'app/controllers/application_controller.rb', line 255

def back_url
  params[:back_url] || request.env['HTTP_REFERER']
end

#check_if_login_requiredObject

check if login is globally required to access the application



99
100
101
102
103
# File 'app/controllers/application_controller.rb', line 99

def 
  # no check needed if user is already logged in
  return true if User.current.logged?
   if Setting.
end

#check_project_privacyObject

make sure that the user is a member of the project (or admin) if project is private used as a before_filter for actions that do not require any particular permission on the project



241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'app/controllers/application_controller.rb', line 241

def check_project_privacy
  if @project && @project.active?
    if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
      true
    else
      User.current.logged? ? render_403 : 
    end
  else
    @project = nil
    render_404
    false
  end
end

#check_project_uniquenessObject

Check if project is unique before bulk operations



231
232
233
234
235
236
237
# File 'app/controllers/application_controller.rb', line 231

def check_project_uniqueness
  unless @project
    # TODO: let users bulk edit/move/destroy issues from different projects
    render_error 'Can not bulk edit/move/destroy issues from different projects'
    return false
  end
end

#default_template(action_name = self.action_name) ⇒ Object

Overrides #default_template so that the api template is used automatically if it exists



465
466
467
468
469
470
471
472
473
474
475
# File 'app/controllers/application_controller.rb', line 465

def default_template(action_name = self.action_name)
  if api_request?
    begin
      return self.view_paths.find_template(default_template_name(action_name), 'api')
    rescue ::ActionView::MissingTemplate
      # the api template was not found
      # fallback to the default behaviour
    end
  end
  super
end

#delete_broken_cookiesObject



31
32
33
34
35
36
37
# File 'app/controllers/application_controller.rb', line 31

def delete_broken_cookies
  if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
    cookies.delete '_redmine_session'    
    redirect_to home_path
    return false
  end
end

#deny_accessObject



150
151
152
# File 'app/controllers/application_controller.rb', line 150

def deny_access
  User.current.logged? ? render_403 : 
end

#filename_for_content_disposition(name) ⇒ Object

Returns a string that can be used as filename value in Content-Disposition header



398
399
400
# File 'app/controllers/application_controller.rb', line 398

def filename_for_content_disposition(name)
  request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
end

#find_current_userObject

Returns the current user or nil if no user is logged in and starts a session if needed



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'app/controllers/application_controller.rb', line 62

def find_current_user
  if session[:user_id]
    # existing session
    (User.active.find(session[:user_id]) rescue nil)
  elsif cookies[:autologin] && Setting.autologin?
    # auto-login feature starts a new session
    user = User.try_to_autologin(cookies[:autologin])
    session[:user_id] = user.id if user
    user
  elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
    # RSS key authentication does not start a session
    User.find_by_rss_key(params[:key])
  elsif Setting.rest_api_enabled? && api_request?
    if (key = api_key_from_request) && accept_key_auth_actions.include?(params[:action])
      # Use API key
      User.find_by_api_key(key)
    else
      # HTTP Basic, either username/password or API key/random
      authenticate_with_http_basic do |username, password|
        User.(username, password) || User.find_by_api_key(username)
      end
    end
  end
end

#find_issuesObject

Filter for bulk issue operations



221
222
223
224
225
226
227
228
# File 'app/controllers/application_controller.rb', line 221

def find_issues
  @issues = Issue.find_all_by_id(params[:id] || params[:ids])
  raise ActiveRecord::RecordNotFound if @issues.empty?
  @projects = @issues.collect(&:project).compact.uniq
  @project = @projects.first if @projects.size == 1
rescue ActiveRecord::RecordNotFound
  render_404
end

#find_model_objectObject



206
207
208
209
210
211
212
213
214
# File 'app/controllers/application_controller.rb', line 206

def find_model_object
  model = self.class.read_inheritable_attribute('model_object')
  if model
    @object = model.find(params[:id])
    self.instance_variable_set('@' + controller_name.singularize, @object) if @object
  end
rescue ActiveRecord::RecordNotFound
  render_404
end

#find_optional_projectObject

Find a project based on params TODO: some subclasses override this, see about merging their logic



189
190
191
192
193
194
195
# File 'app/controllers/application_controller.rb', line 189

def find_optional_project
  @project = Project.find(params[:project_id]) unless params[:project_id].blank?
  allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
  allowed ? true : deny_access
rescue ActiveRecord::RecordNotFound
  render_404
end

#find_projectObject

Find project of id params



174
175
176
177
178
# File 'app/controllers/application_controller.rb', line 174

def find_project
  @project = Project.find(params[:id])
rescue ActiveRecord::RecordNotFound
  render_404
end

#find_project_by_project_idObject

Find project of id params



181
182
183
184
185
# File 'app/controllers/application_controller.rb', line 181

def find_project_by_project_id
  @project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound
  render_404
end

#find_project_from_associationObject

Finds and sets @project based on @object.project



198
199
200
201
202
203
204
# File 'app/controllers/application_controller.rb', line 198

def find_project_from_association
  render_404 unless @object.present?
  
  @project = @object.project
rescue ActiveRecord::RecordNotFound
  render_404
end

#invalid_authenticity_tokenObject



313
314
315
316
317
318
# File 'app/controllers/application_controller.rb', line 313

def invalid_authenticity_token
  if api_request?
    logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
  end
  render_error "Invalid form authenticity token."
end

#logged_user=(user) ⇒ Object

Sets the logged in user



88
89
90
91
92
93
94
95
96
# File 'app/controllers/application_controller.rb', line 88

def logged_user=(user)
  reset_session
  if user && user.is_a?(User)
    User.current = user
    session[:user_id] = user.id
  else
    User.current = User.anonymous
  end
end

#object_errors_to_json(object) ⇒ Object

Converts the errors on an ActiveRecord object into a common JSON format



444
445
446
447
448
# File 'app/controllers/application_controller.rb', line 444

def object_errors_to_json(object)
  object.errors.collect do |attribute, error|
    { attribute => error }
  end.to_json
end

#parse_qvalues(value) ⇒ Object

qvalues http header parser code taken from webrick



378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'app/controllers/application_controller.rb', line 378

def parse_qvalues(value)
  tmp = []
  if value
    parts = value.split(/,\s*/)
    parts.each {|part|
      if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
        val = m[1]
        q = (m[2] or 1).to_f
        tmp.push([val, q])
      end
    }
    tmp = tmp.sort_by{|val, q| -q}
    tmp.collect!{|val, q| val}
  end
  return tmp
rescue
  nil
end

#per_page_optionObject

Returns the number of objects that should be displayed on the paginated list



339
340
341
342
343
344
345
346
347
348
349
350
# File 'app/controllers/application_controller.rb', line 339

def per_page_option
  per_page = nil
  if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
    per_page = params[:per_page].to_s.to_i
    session[:per_page] = per_page
  elsif session[:per_page]
    per_page = session[:per_page]
  else
    per_page = Setting.per_page_options_array.first || 25
  end
  per_page
end

#pick_layout(*args) ⇒ Object

Overrides #pick_layout so that #render with no arguments doesn't use the layout for api requests



479
480
481
# File 'app/controllers/application_controller.rb', line 479

def pick_layout(*args)
  api_request? ? nil : super
end

#query_statement_invalid(exception) ⇒ Object

Rescues an invalid query statement. Just in case...



436
437
438
439
440
441
# File 'app/controllers/application_controller.rb', line 436

def query_statement_invalid(exception)
  logger.error "Query::StatementInvalid: #{exception.message}" if logger
  session.delete(:query)
  sort_clear if respond_to?(:sort_clear)
  render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
end

#redirect_back_or_default(default) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'app/controllers/application_controller.rb', line 259

def redirect_back_or_default(default)
  back_url = CGI.unescape(params[:back_url].to_s)
  if !back_url.blank?
    begin
      uri = URI.parse(back_url)
      # do not redirect user to another host or to the login or register page
      if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
        redirect_to(back_url)
        return
      end
    rescue URI::InvalidURIError
      # redirect to default
    end
  end
  redirect_to default
end

#render_403(options = {}) ⇒ Object



276
277
278
279
280
# File 'app/controllers/application_controller.rb', line 276

def render_403(options={})
  @project = nil
  render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
  return false
end

#render_404(options = {}) ⇒ Object



282
283
284
285
# File 'app/controllers/application_controller.rb', line 282

def render_404(options={})
  render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
  return false
end

#render_attachment_warning_if_needed(obj) ⇒ Object

Renders a warning flash if obj has unsaved attachments



416
417
418
# File 'app/controllers/application_controller.rb', line 416

def render_attachment_warning_if_needed(obj)
  flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
end

#render_error(arg) ⇒ Object

Renders an error response



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'app/controllers/application_controller.rb', line 288

def render_error(arg)
  arg = {:message => arg} unless arg.is_a?(Hash)
  
  @message = arg[:message]
  @message = l(@message) if @message.is_a?(Symbol)
  @status = arg[:status] || 500
  
  respond_to do |format|
    format.html {
      render :template => 'common/error', :layout => use_layout, :status => @status
    }
    format.atom { head @status }
    format.xml { head @status }
    format.js { head @status }
    format.json { head @status }
  end
end

#render_feed(items, options = {}) ⇒ Object



320
321
322
323
324
325
326
# File 'app/controllers/application_controller.rb', line 320

def render_feed(items, options={})    
  @items = items || []
  @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
  @items = @items.slice(0, Setting.feeds_limit.to_i)
  @title = options[:title] || Setting.app_title
  render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
end

#render_validation_errors(object) ⇒ Object

Renders API response on validation failure



451
452
453
454
455
456
457
458
459
460
461
# File 'app/controllers/application_controller.rb', line 451

def render_validation_errors(object)
  options = { :status => :unprocessable_entity, :layout => false }
  options.merge!(case params[:format]
    when 'xml';  { :xml =>  object.errors }
    when 'json'; { :json => {'errors' => object.errors} } # ActiveResource client compliance
    else
      raise "Unknown format #{params[:format]} in #render_validation_errors"
    end
  )
  render options
end

#require_adminObject



141
142
143
144
145
146
147
148
# File 'app/controllers/application_controller.rb', line 141

def require_admin
  return unless 
  if !User.current.admin?
    render_403
    return false
  end
  true
end

#require_loginObject



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'app/controllers/application_controller.rb', line 121

def 
  if !User.current.logged?
    # Extract only the basic url parameters on non-GET requests
    if request.get?
      url = url_for(params)
    else
      url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
    end
    respond_to do |format|
      format.html { redirect_to :controller => "account", :action => "login", :back_url => url }
      format.atom { redirect_to :controller => "account", :action => "login", :back_url => url }
      format.xml  { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
      format.js   { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
      format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
    end
    return false
  end
  true
end

#set_flash_from_bulk_issue_save(issues, unsaved_issue_ids) ⇒ Object

Sets the `flash` notice or error based the number of issues that did not save



424
425
426
427
428
429
430
431
432
433
# File 'app/controllers/application_controller.rb', line 424

def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
  if unsaved_issue_ids.empty?
    flash[:notice] = l(:notice_successful_update) unless issues.empty?
  else
    flash[:error] = l(:notice_failed_to_save_issues,
                      :count => unsaved_issue_ids.size,
                      :total => issues.size,
                      :ids => '#' + unsaved_issue_ids.join(', #'))
  end
end

#set_localizationObject



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'app/controllers/application_controller.rb', line 105

def set_localization
  lang = nil
  if User.current.logged?
    lang = find_language(User.current.language)
  end
  if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
    accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first
    if !accept_lang.blank?
      accept_lang = accept_lang.downcase
      lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
    end
  end
  lang ||= Setting.default_language
  set_language_if_valid(lang)
end

#use_layoutboolean, string

Picks which layout to use based on the request



309
310
311
# File 'app/controllers/application_controller.rb', line 309

def use_layout
  request.xhr? ? false : 'base'
end

#user_setupObject



53
54
55
56
57
58
# File 'app/controllers/application_controller.rb', line 53

def user_setup
  # Check the settings cache for each request
  Setting.check_cache
  # Find the current user
  User.current = find_current_user
end