mirror of
https://github.com/discourse/discourse.git
synced 2026-08-06 13:08:40 +08:00
This means that we no longer need to recompile theme JS bundles when theme settings are changed. It has parity with site settings, which are already included in the preload data. Theme#cached_settings is already cached in-memory, so performance impact is minimal. Since theme JS bundles are now only dependent on `extra_js` ThemeFields, we can make their caching much more stricter, meaning that they don't need to be recompiled on every single `theme.save!`. Now they will only be invalidated when extra_js fields actually change, or when the theme compiler_version changes.
165 lines
4.8 KiB
Ruby
Vendored
165 lines
4.8 KiB
Ruby
Vendored
# 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
|
|
|
|
preload_upcoming_change_data(@guardian.user)
|
|
|
|
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_#{I18n.locale}") do
|
|
topic = Topic.where(archetype: Archetype.banner).first
|
|
banner = topic.present? && !topic.category&.read_restricted? ? topic.banner(@guardian) : {}
|
|
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?,
|
|
description: plugin.metadata.about,
|
|
}
|
|
end,
|
|
)
|
|
end
|
|
end
|
|
|
|
def preload_anonymous_data
|
|
check_readonly_mode if @readonly_mode.nil?
|
|
@preloaded["site"] = Site.json_for(@guardian)
|
|
@preloaded["siteSettings"] = SiteSetting.client_settings_json
|
|
@preloaded["themeSiteSettingOverrides"] = SiteSetting.theme_site_settings_json(@theme_id)
|
|
@preloaded["customHTML"] = custom_html_json
|
|
@preloaded["banner"] = banner_json
|
|
@preloaded["customEmoji"] = custom_emoji
|
|
@preloaded["isReadOnly"] = @readonly_mode.to_json
|
|
@preloaded["isStaffWritesOnly"] = @staff_writes_only_mode.to_json
|
|
@preloaded["activatedThemes"] = activated_themes_json
|
|
end
|
|
|
|
def preload_upcoming_change_data(user)
|
|
@preloaded["upcomingChanges"] = SiteSetting
|
|
.upcoming_change_site_settings
|
|
.each_with_object({}) do |upcoming_change, hash|
|
|
hash[upcoming_change] = UpcomingChanges.enabled_for_user?(upcoming_change, user)
|
|
end
|
|
.to_json
|
|
end
|
|
|
|
def activated_themes_json
|
|
id = @theme_id
|
|
return "{}" if id.blank?
|
|
ids = Theme.transform_ids(id)
|
|
Theme
|
|
.where(id: ids)
|
|
.each_with_object({}) do |theme, hash|
|
|
hash[theme.id] = { name: theme.name, settings: theme.cached_settings }
|
|
end
|
|
.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
|