summaryrefslogtreecommitdiff
path: root/app/controllers/application_controller.rb
blob: 61ced219eaf1fd09274eb7bc0d23a9f3557d8dfd (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
class ApplicationController < ActionController::Base
  protect_from_forgery
  before_action :check_mime_types
  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 CouchRest::Exception, :with => :default_error_handler

  ActiveSupport.run_load_hooks(:application_controller, self)

  # by default we only respond to html.
  # If you want to respond with json you are probably working on
  # an ApiController.
  respond_to :html

  protected

  # UPGRADE: this won't be needed in Rails 5 anymore as it's the default
  # behavior if a template is present but a different format would be
  # rendered and that template is not present
  def check_mime_types
    mimes = collect_mimes_from_class_level()
    return if mimes.empty?

    collector = ActionController::MimeResponds::Collector.new(mimes, request.variant)
    unless collector.negotiate_format(request)
      raise ActionController::UnknownFormat
    end
  end

  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) && I18n.locale != I18n.default_locale
      { :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.path !~ NON_LOCALE_PATHS
  end
end