mirror of
https://github.com/discourse/discourse.git
synced 2026-09-05 04:40:41 -05:00
Merge branch 'master' into fix-by-external
Conflicts: app/controllers/users_controller.rb
This commit is contained in:
@@ -8,11 +8,13 @@ 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
|
||||
|
||||
@@ -40,6 +42,7 @@ class ApplicationController < ActionController::Base
|
||||
before_filter :preload_json
|
||||
before_filter :check_xhr
|
||||
before_filter :redirect_to_login_if_required
|
||||
after_filter :add_readonly_header
|
||||
|
||||
layout :set_layout
|
||||
|
||||
@@ -51,6 +54,10 @@ class ApplicationController < ActionController::Base
|
||||
@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
|
||||
@@ -59,24 +66,10 @@ class ApplicationController < ActionController::Base
|
||||
use_crawler_layout? ? 'crawler' : 'application'
|
||||
end
|
||||
|
||||
rescue_from Exception do |exception|
|
||||
unless [ActiveRecord::RecordNotFound,
|
||||
ActionController::RoutingError,
|
||||
ActionController::UnknownController,
|
||||
AbstractController::ActionNotFound].include? exception.class
|
||||
begin
|
||||
ErrorLog.report_async!(exception, self, request, current_user)
|
||||
rescue
|
||||
# dont care give up
|
||||
end
|
||||
end
|
||||
raise
|
||||
end
|
||||
|
||||
# Some exceptions
|
||||
class RenderEmpty < Exception; end
|
||||
class RenderEmpty < StandardError; end
|
||||
|
||||
# Render nothing unless we are an xhr request
|
||||
# Render nothing
|
||||
rescue_from RenderEmpty do
|
||||
render 'default/empty'
|
||||
end
|
||||
@@ -93,40 +86,58 @@ class ApplicationController < ActionController::Base
|
||||
time_left = I18n.t("rate_limiter.hours", count: (e.available_in / 1.hour.to_i))
|
||||
end
|
||||
|
||||
render json: {errors: [I18n.t("rate_limiter.too_many_requests", time_left: time_left)]}, status: 429
|
||||
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.get?
|
||||
redirect_to "/"
|
||||
if (request.format && request.format.json?) || request.xhr? || !request.get?
|
||||
rescue_discourse_actions(:not_logged_in, 403, true)
|
||||
else
|
||||
render status: 403, json: failed_json.merge(message: I18n.t(:not_logged_in))
|
||||
redirect_to path("/")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
rescue_from Discourse::NotFound do
|
||||
rescue_discourse_actions("[error: 'not found']", 404) # TODO: this breaks json responses
|
||||
rescue_discourse_actions(:not_found, 404)
|
||||
end
|
||||
|
||||
rescue_from Discourse::InvalidAccess do
|
||||
rescue_discourse_actions("[error: 'invalid access']", 403, true) # TODO: this breaks json responses
|
||||
rescue_discourse_actions(:invalid_access, 403, true)
|
||||
end
|
||||
|
||||
rescue_from Discourse::ReadOnly do
|
||||
render status: 405, json: failed_json.merge(message: I18n.t("read_only_mode_enabled"))
|
||||
render_json_error I18n.t('read_only_mode_enabled'), type: :read_only, status: 405
|
||||
end
|
||||
|
||||
def rescue_discourse_actions(message, error, include_ember=false)
|
||||
if request.format && request.format.json?
|
||||
# TODO: this doesn't make sense. Stuffing an html page into a json response will cause
|
||||
# $.parseJSON to fail in the browser. Also returning text like "[error: 'invalid access']"
|
||||
# from the above rescue_from blocks will fail because that isn't valid json.
|
||||
render status: error, layout: false, text: (error == 404) ? build_not_found_page(error) : message
|
||||
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(error, include_ember ? 'application' : 'no_ember')
|
||||
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
|
||||
|
||||
@@ -137,8 +148,8 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def set_locale
|
||||
I18n.locale = if SiteSetting.allow_user_locale && current_user && current_user.locale.present?
|
||||
current_user.locale
|
||||
I18n.locale = if current_user
|
||||
current_user.effective_locale
|
||||
else
|
||||
SiteSetting.default_locale
|
||||
end
|
||||
@@ -197,9 +208,9 @@ class ApplicationController < ActionController::Base
|
||||
@guardian ||= Guardian.new(current_user)
|
||||
end
|
||||
|
||||
def serialize_data(obj, serializer, opts={})
|
||||
def serialize_data(obj, serializer, opts=nil)
|
||||
# If it's an array, apply the serializer as an each_serializer to the elements
|
||||
serializer_opts = {scope: guardian}.merge!(opts)
|
||||
serializer_opts = {scope: guardian}.merge!(opts || {})
|
||||
if obj.respond_to?(:to_ary)
|
||||
serializer_opts[:each_serializer] = serializer
|
||||
ActiveModel::ArraySerializer.new(obj.to_ary, serializer_opts).as_json
|
||||
@@ -212,12 +223,21 @@ class ApplicationController < ActionController::Base
|
||||
# 20% slower than calling MultiJSON.dump ourselves. I'm not sure why
|
||||
# Rails doesn't call MultiJson.dump when you pass it json: obj but
|
||||
# it seems we don't need whatever Rails is doing.
|
||||
def render_serialized(obj, serializer, opts={})
|
||||
render_json_dump(serialize_data(obj, serializer, opts))
|
||||
def render_serialized(obj, serializer, opts=nil)
|
||||
render_json_dump(serialize_data(obj, serializer, opts), opts)
|
||||
end
|
||||
|
||||
def render_json_dump(obj)
|
||||
render json: MultiJson.dump(obj)
|
||||
def render_json_dump(obj, opts=nil)
|
||||
opts ||= {}
|
||||
if opts[:rest_serializer]
|
||||
obj['__rest_serializer'] = "1"
|
||||
opts.each do |k, v|
|
||||
obj[k] = v if k.to_s.start_with?("refresh_")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
render json: MultiJson.dump(obj), status: opts[:status] || 200
|
||||
end
|
||||
|
||||
def can_cache_content?
|
||||
@@ -242,7 +262,7 @@ class ApplicationController < ActionController::Base
|
||||
external_id = params[:external_id].gsub(/\.json$/, '')
|
||||
SingleSignOnRecord.find_by(external_id: external_id).try(:user)
|
||||
end
|
||||
raise Discourse::NotFound.new if user.blank?
|
||||
raise Discourse::NotFound if user.blank?
|
||||
|
||||
guardian.ensure_can_see!(user)
|
||||
user
|
||||
@@ -275,9 +295,10 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def custom_html_json
|
||||
target = view_context.mobile_view? ? :mobile : :desktop
|
||||
data = {
|
||||
top: SiteCustomization.custom_top(session[:preview_style]),
|
||||
footer: SiteCustomization.custom_footer(session[:preview_style])
|
||||
top: SiteCustomization.custom_top(session[:preview_style], target),
|
||||
footer: SiteCustomization.custom_footer(session[:preview_style], target)
|
||||
}
|
||||
|
||||
if DiscoursePluginRegistry.custom_html
|
||||
@@ -308,8 +329,15 @@ class ApplicationController < ActionController::Base
|
||||
MultiJson.dump(serializer)
|
||||
end
|
||||
|
||||
def render_json_error(obj)
|
||||
render json: MultiJson.dump(create_errors_json(obj)), status: 422
|
||||
# Render action for a JSON error.
|
||||
#
|
||||
# obj - a translated string, an ActiveRecord model, or an array of translated strings
|
||||
# opts:
|
||||
# type - a machine-readable description of the error
|
||||
# status - HTTP status code to return
|
||||
def render_json_error(obj, opts={})
|
||||
opts = { status: opts } if opts.is_a?(Fixnum)
|
||||
render json: MultiJson.dump(create_errors_json(obj, opts[:type])), status: opts[:status] || 422
|
||||
end
|
||||
|
||||
def success_json
|
||||
@@ -362,21 +390,34 @@ class ApplicationController < ActionController::Base
|
||||
raise Discourse::NotLoggedIn.new unless current_user.present?
|
||||
end
|
||||
|
||||
def ensure_staff
|
||||
raise Discourse::InvalidAccess.new unless current_user && current_user.staff?
|
||||
end
|
||||
|
||||
def redirect_to_login_if_required
|
||||
return if current_user || (request.format.json? && api_key_valid?)
|
||||
|
||||
# save original URL in a cookie
|
||||
cookies[:destination_url] = request.original_url unless request.original_url =~ /uploads/
|
||||
redirect_to :login if SiteSetting.login_required?
|
||||
|
||||
# redirect user to the SSO page if we need to log in AND SSO is enabled
|
||||
if SiteSetting.login_required?
|
||||
if SiteSetting.enable_sso?
|
||||
redirect_to path('/session/sso')
|
||||
else
|
||||
redirect_to :login
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def block_if_readonly_mode
|
||||
return if request.fullpath.start_with?("/admin/backups")
|
||||
raise Discourse::ReadOnly.new if !request.get? && Discourse.readonly_mode?
|
||||
return if request.fullpath.start_with?(path "/admin/backups")
|
||||
raise Discourse::ReadOnly.new if !(request.get? || request.head?) && Discourse.readonly_mode?
|
||||
end
|
||||
|
||||
def build_not_found_page(status=404, layout=false)
|
||||
category_topic_ids = Category.pluck(:topic_id).compact
|
||||
@container_class = "container not-found-container"
|
||||
@top_viewed = Topic.where.not(id: category_topic_ids).top_viewed(10)
|
||||
@recent = Topic.where.not(id: category_topic_ids).recent(10)
|
||||
@slug = params[:slug].class == String ? params[:slug] : ''
|
||||
|
||||
Reference in New Issue
Block a user