mirror of
https://github.com/discourse/discourse.git
synced 2025-02-24 02:10:53 +08:00

This commit drops the `before_action :preload_json` callback in `ApplicationController` as it adds unnecessary complexity to `ApplicationController` as well as other controllers which has to skip this callback. The source of the complexity comes mainly from the following two conditionals in the `preload_json` method: ``` # We don't preload JSON on xhr or JSON request return if request.xhr? || request.format.json? # if we are posting in makes no sense to preload return if request.method != "GET" ``` Basically, the conditionals solely exists for optimization purposes to ensure that we don't run the preloading code when the request is not a GET request and the response is not expected to be HTML. The key problem here is that the conditionals are trying to expect what the content type of the response will be and this has proven to be hard to get right. Instead, we can simplify this problem by running the preloading code in a more deterministic way which is to preload only when the `application` layout is being rendered and this is main change that this commit introduces.
147 lines
4.1 KiB
Ruby
147 lines
4.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class ApplicationLayoutPreloader
|
|
include ReadOnlyMixin
|
|
|
|
def self.banner_json_cache
|
|
@banner_json_cache ||= DistributedCache.new("banner_json")
|
|
end
|
|
|
|
def initialize(guardian:, theme_id:, theme_target:, login_method:)
|
|
@guardian = guardian
|
|
@theme_id = theme_id
|
|
@theme_target = theme_target
|
|
@login_method = login_method
|
|
@preloaded = {}
|
|
end
|
|
|
|
def store_preloaded(key, json)
|
|
# 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 safety deeper in the library or even in AM serializer
|
|
@preloaded[key] = json.gsub("</", "<\\/")
|
|
end
|
|
|
|
def preloaded_data
|
|
preload_anonymous_data
|
|
|
|
if @guardian.authenticated?
|
|
@guardian.user.sync_notification_channel_position
|
|
preload_current_user_data
|
|
end
|
|
|
|
@preloaded
|
|
end
|
|
|
|
def banner_json
|
|
return "{}" if !@guardian.authenticated? && SiteSetting.login_required?
|
|
|
|
self
|
|
.class
|
|
.banner_json_cache
|
|
.defer_get_set("json") do
|
|
topic = Topic.where(archetype: Archetype.banner).first
|
|
banner = topic.present? ? topic.banner : {}
|
|
MultiJson.dump(banner)
|
|
end
|
|
end
|
|
|
|
def custom_html_json
|
|
data =
|
|
if @theme_id.present?
|
|
{
|
|
top: Theme.lookup_field(@theme_id, @theme_target, "after_header"),
|
|
footer: Theme.lookup_field(@theme_id, @theme_target, "footer"),
|
|
}
|
|
else
|
|
{}
|
|
end
|
|
|
|
data.merge! DiscoursePluginRegistry.custom_html if DiscoursePluginRegistry.custom_html
|
|
|
|
DiscoursePluginRegistry.html_builders.each do |name, _|
|
|
if name.start_with?("client:")
|
|
data[name.sub(/\Aclient:/, "")] = DiscoursePluginRegistry.build_html(name, self)
|
|
end
|
|
end
|
|
|
|
MultiJson.dump(data)
|
|
end
|
|
|
|
private
|
|
|
|
def preload_current_user_data
|
|
@preloaded["currentUser"] = MultiJson.dump(
|
|
CurrentUserSerializer.new(
|
|
@guardian.user,
|
|
scope: @guardian,
|
|
root: false,
|
|
login_method: @login_method,
|
|
),
|
|
)
|
|
|
|
report = TopicTrackingState.report(@guardian.user)
|
|
serializer = TopicTrackingStateSerializer.new(report, scope: @guardian, root: false)
|
|
hash = serializer.as_json
|
|
|
|
@preloaded["topicTrackingStates"] = MultiJson.dump(hash[:data])
|
|
@preloaded["topicTrackingStateMeta"] = MultiJson.dump(hash[:meta])
|
|
|
|
if @guardian.is_admin?
|
|
# This is used in the wizard so we can preload fonts using the FontMap JS API.
|
|
@preloaded["fontMap"] = MultiJson.dump(load_font_map)
|
|
|
|
# Used to show plugin-specific admin routes in the sidebar.
|
|
@preloaded["visiblePlugins"] = MultiJson.dump(
|
|
Discourse
|
|
.plugins_sorted_by_name(enabled_only: false)
|
|
.map do |plugin|
|
|
{
|
|
name: plugin.name.downcase,
|
|
humanized_name: plugin.humanized_name,
|
|
admin_route: plugin.full_admin_route,
|
|
enabled: plugin.enabled?,
|
|
}
|
|
end,
|
|
)
|
|
end
|
|
end
|
|
|
|
def preload_anonymous_data
|
|
@preloaded["site"] = Site.json_for(@guardian)
|
|
@preloaded["siteSettings"] = SiteSetting.client_settings_json
|
|
@preloaded["customHTML"] = custom_html_json
|
|
@preloaded["banner"] = banner_json
|
|
@preloaded["customEmoji"] = custom_emoji
|
|
@preloaded["isReadOnly"] = get_or_check_readonly_mode.to_json
|
|
@preloaded["isStaffWritesOnly"] = get_or_check_staff_writes_only_mode.to_json
|
|
@preloaded["activatedThemes"] = activated_themes_json
|
|
end
|
|
|
|
def activated_themes_json
|
|
id = @theme_id
|
|
return "{}" if id.blank?
|
|
ids = Theme.transform_ids(id)
|
|
Theme.where(id: ids).pluck(:id, :name).to_h.to_json
|
|
end
|
|
|
|
def load_font_map
|
|
DiscourseFonts
|
|
.fonts
|
|
.each_with_object({}) do |font, font_map|
|
|
next if !font[:variants]
|
|
font_map[font[:key]] = font[:variants].map do |v|
|
|
{
|
|
url: "#{Discourse.base_url}/fonts/#{v[:filename]}?v=#{DiscourseFonts::VERSION}",
|
|
weight: v[:weight],
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
def custom_emoji
|
|
serializer = ActiveModel::ArraySerializer.new(Emoji.custom, each_serializer: EmojiSerializer)
|
|
MultiJson.dump(serializer)
|
|
end
|
|
end
|