require 'current_user' require_dependency 'canonical_url' require_dependency 'discourse' require_dependency 'custom_renderer' require_dependency 'archetype' require_dependency 'rate_limiter' require_dependency 'crawler_detection' require_dependency 'json_error' require_dependency 'letter_avatar' require_dependency 'distributed_cache' require_dependency 'global_path' class ApplicationController < ActionController::Base include CurrentUser include CanonicalURL::ControllerExtensions include JsonError include GlobalPath serialization_scope :guardian protect_from_forgery # Default Rails 3.2 lets the request through with a blank session # we are being more pedantic here and nulling session / current_user # and then raising a CSRF exception def handle_unverified_request # NOTE: API key is secret, having it invalidates the need for a CSRF token unless is_api? super clear_current_user render text: "['BAD CSRF']", status: 403 end end before_filter :set_current_user_for_logs before_filter :set_locale before_filter :set_mobile_view before_filter :inject_preview_style before_filter :disable_customization before_filter :block_if_readonly_mode before_filter :authorize_mini_profiler before_filter :preload_json before_filter :redirect_to_login_if_required before_filter :check_xhr after_filter :add_readonly_header layout :set_layout def has_escaped_fragment? SiteSetting.enable_escaped_fragments? && params.key?("_escaped_fragment_") end def use_crawler_layout? @use_crawler_layout ||= (has_escaped_fragment? || CrawlerDetection.crawler?(request.user_agent)) end def add_readonly_header response.headers['Discourse-Readonly'] = 'true' if Discourse.readonly_mode? end def slow_platform? request.user_agent =~ /Android/ end def set_layout use_crawler_layout? ? 'crawler' : 'application' end # Some exceptions class RenderEmpty < StandardError; end # Render nothing rescue_from RenderEmpty do render 'default/empty' end # If they hit the rate limiter rescue_from RateLimiter::LimitExceeded do |e| time_left = "" if e.available_in < 1.minute.to_i time_left = I18n.t("rate_limiter.seconds", count: e.available_in) elsif e.available_in < 1.hour.to_i time_left = I18n.t("rate_limiter.minutes", count: (e.available_in / 1.minute.to_i)) else time_left = I18n.t("rate_limiter.hours", count: (e.available_in / 1.hour.to_i)) end render_json_error I18n.t("rate_limiter.too_many_requests", time_left: time_left), type: :rate_limit, status: 429 end rescue_from PG::ReadOnlySqlTransaction do |e| Discourse.received_readonly! raise Discourse::ReadOnly end rescue_from Discourse::NotLoggedIn do |e| raise e if Rails.env.test? if (request.format && request.format.json?) || request.xhr? || !request.get? rescue_discourse_actions(:not_logged_in, 403, true) else redirect_to path("/") end end rescue_from Discourse::NotFound do rescue_discourse_actions(:not_found, 404) end rescue_from Discourse::InvalidAccess do rescue_discourse_actions(:invalid_access, 403, true) end rescue_from Discourse::ReadOnly do render_json_error I18n.t('read_only_mode_enabled'), type: :read_only, status: 405 end def rescue_discourse_actions(type, status_code, include_ember=false) if (request.format && request.format.json?) || (request.xhr?) # HACK: do not use render_json_error for topics#show if request.params[:controller] == 'topics' && request.params[:action] == 'show' return render status: status_code, layout: false, text: (status_code == 404) ? build_not_found_page(status_code) : I18n.t(type) end render_json_error I18n.t(type), type: type, status: status_code else render text: build_not_found_page(status_code, include_ember ? 'application' : 'no_ember') end end class PluginDisabled < StandardError; end # If a controller requires a plugin, it will raise an exception if that plugin is # disabled. This allows plugins to be disabled programatically. def self.requires_plugin(plugin_name) before_filter do raise PluginDisabled.new if Discourse.disabled_plugin_names.include?(plugin_name) end end def set_current_user_for_logs if current_user Logster.add_to_env(request.env,"username",current_user.username) end end def set_locale I18n.locale = if current_user current_user.effective_locale else SiteSetting.default_locale end end def store_preloaded(key, json) @preloaded ||= {} # I dislike that there is a gsub as opposed to a gsub! # but we can not be mucking with user input, I wonder if there is a way # to inject this safty deeper in the library or even in AM serializer @preloaded[key] = json.gsub("