summaryrefslogtreecommitdiff
path: root/app/controllers/application_controller.rb
blob: 58e6f83f9c689d880dca84a93ffe41ab92cdd019 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter :set_locale
  before_filter :no_cache_header
  before_filter :no_frame_header
  before_filter :language_header
  rescue_from StandardError, :with => :default_error_handler
  rescue_from RestClient::Exception, :with => :default_error_handler

  ActiveSupport.run_load_hooks(:application_controller, self)

  protected

  def default_error_handler(exc)
    respond_to do |format|
      format.json { render_json_error(exc) }
      format.all  { raise exc }  # reraise the exception so the normal thing happens.
    end
  end

  #
  # I think this should be 'errors', not 'error', since that is what
  # `respond_with @object` will return. For now, I am leaving this as 'error',
  # since there is some code that depends on this.
  #
  def render_json_error(e)
    Rails.logger.error e
    Rails.logger.error e.backtrace.join("\n")
    if e.is_a?(CouchRest::StorageMissing)
      render status: 500, json: {error: "The database '#{e.db}' does not exist!"}
    else
      render status: 500, json: {error: "The server failed to process your request. We'll look into it (#{e.class})."}
    end
  end

  ##
  ## LOCALE
  ##

  #
  # Before filter to set the current locale. Possible outcomes:
  #
  #   (a) do nothing for certain routes and requests.
  #   (b) if path already starts with locale, set I18n.locale and default_url_options.
  #   (c) otherwise, redirect so that path starts with locale.
  #
  def set_locale
    if request_may_have_locale?(request)
      if CommonLanguages::available_code?(params[:locale])
        I18n.locale = params[:locale]
      else
        I18n.locale = http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
        if I18n.locale != I18n.default_locale
          redirect_to url_for(params.merge(:locale => I18n.locale))
        end
      end
    end
  end

  def default_url_options(options={})
    if request_may_have_locale?(request)
      { :locale => I18n.locale }
    else
      { :locale => nil }
    end
  end

  ##
  ## HTTP HEADERS
  ## These are in individual helpers so that controllers can disable them if needed.
  ##

  #
  # Not necessary, but kind to let the browser know the current locale.
  #
  def language_header
    response.headers["Content-Language"] = I18n.locale.to_s
  end

  #
  # prevent app from being embedded in an iframe, for browsers that support x-frame-options.
  #
  def no_frame_header
    response.headers["X-Frame-Options"] = "DENY"
  end

  #
  # we want to prevent the browser from caching anything, just to be safe.
  #
  def no_cache_header
    response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "0"
  end

  private

  #
  # URL paths for which we don't enforce the locale as the prefix of the path.
  #
  NON_LOCALE_PATHS = /^\/(assets|webfinger|.well-known|rails|key|[0-9]+)($|\/)/

  #
  # For some requests, we ignore locale determination.
  #
  def request_may_have_locale?(request)
    request.method == "GET" && request.format == 'text/html' && request.path !~ NON_LOCALE_PATHS
  end
end