mirror of
https://github.com/discourse/discourse.git
synced 2026-08-06 13:08:40 +08:00
This commit introduces automatic CSS classes added to the <body> based on the upcoming changes the current visitor (logged in or anon user) is affected by. This allows designers and theme developers to easily style the UI based on upcoming changes. For example a change called `enable_chat_channel_starring` would be `uc-enable-chat-channel-starring` on the body. This commit also refactors the way we send upcoming change data to the UI for the current user, because it also needs to work for anon users. Only upcoming changes enabled for Everyone will be added to the <body> for anon users. Logged in in users will get classes for all upcoming changes they are affected by based on their groups or changes enabled for Everyone.
160 lines
4.6 KiB
Ruby
Vendored
160 lines
4.6 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") do
|
|
topic = Topic.where(archetype: Archetype.banner).first
|
|
banner = topic.present? ? 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).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
|