mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-06 05:24:51 +08:00
This allows themes to set a modifier in their `about.json` that
restricts a theme's color palettes to only what's defined within the
theme.
```json
"modifiers": {
"only_theme_color_schemes": true
}
```
Once set, a theme's color palette settings will only list the palettes
included in about.json. A banner is shown indicating that the theme
restricts palettes.
<img width="700" alt="image"
src="https://github.com/user-attachments/assets/cfb4433c-c7c9-4923-a121-fddd572c29ea"
/>
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/67ec1f72-d408-4e3b-911f-a2d0e4db9a37"
/>
If there's only 1 palette defined by the theme, it will be set to both
light and dark settings.
If there are more than 1 palettes defined, we check to see if one is
dark and will use the first dark palette for the dark setting.
If there are no color palettes defined by the theme, the color selection
will be unrestricted.
---------
Co-authored-by: Gabriel Grubba <70247653+Grubba27@users.noreply.github.com>
462 lines
16 KiB
Ruby
462 lines
16 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe Site do
|
|
after { Site.clear_cache }
|
|
|
|
def expect_correct_themes(guardian)
|
|
json = Site.json_for(guardian)
|
|
parsed = JSON.parse(json)
|
|
|
|
expected =
|
|
Theme
|
|
.where("id = :default OR user_selectable", default: SiteSetting.default_theme_id)
|
|
.order(:name)
|
|
.pluck(:id, :name, :color_scheme_id, :dark_color_scheme_id)
|
|
.map do |id, name, color_scheme_id, dark_color_scheme_id|
|
|
{
|
|
"theme_id" => id,
|
|
"name" => name,
|
|
"default" => id == SiteSetting.default_theme_id,
|
|
"color_scheme_id" => color_scheme_id,
|
|
"dark_color_scheme_id" => dark_color_scheme_id,
|
|
"only_theme_color_schemes" => false,
|
|
}
|
|
end
|
|
|
|
expect(parsed["user_themes"]).to eq(expected)
|
|
end
|
|
|
|
it "includes user themes and expires them as needed" do
|
|
default_theme = Fabricate(:theme)
|
|
SiteSetting.default_theme_id = default_theme.id
|
|
user_theme = Fabricate(:theme, user_selectable: true)
|
|
second_user_theme = Fabricate(:theme, user_selectable: true)
|
|
color_scheme = Fabricate(:color_scheme)
|
|
|
|
anon_guardian = Guardian.new
|
|
user_guardian = Guardian.new(Fabricate(:user))
|
|
|
|
expect_correct_themes(anon_guardian)
|
|
expect_correct_themes(user_guardian)
|
|
|
|
Theme.clear_default!
|
|
|
|
expect_correct_themes(anon_guardian)
|
|
expect_correct_themes(user_guardian)
|
|
|
|
user_theme.user_selectable = false
|
|
user_theme.save!
|
|
|
|
expect_correct_themes(anon_guardian)
|
|
expect_correct_themes(user_guardian)
|
|
|
|
second_user_theme.color_scheme_id = color_scheme.id
|
|
second_user_theme.save!
|
|
|
|
expect_correct_themes(anon_guardian)
|
|
expect_correct_themes(user_guardian)
|
|
end
|
|
|
|
it "returns correct notification level for categories" do
|
|
category = Fabricate(:category)
|
|
guardian = Guardian.new
|
|
expect(Site.new(guardian).categories.last[:notification_level]).to eq(1)
|
|
SiteSetting.mute_all_categories_by_default = true
|
|
expect(Site.new(guardian).categories.last[:notification_level]).to eq(0)
|
|
SiteSetting.default_categories_tracking = category.id.to_s
|
|
expect(Site.new(guardian).categories.last[:notification_level]).to eq(1)
|
|
end
|
|
|
|
describe "#categories" do
|
|
fab!(:category)
|
|
fab!(:user)
|
|
let(:guardian) { Guardian.new(user) }
|
|
|
|
it "omits read restricted categories" do
|
|
expect(Site.new(guardian).categories.map { |c| c[:id] }).to contain_exactly(
|
|
SiteSetting.uncategorized_category_id,
|
|
category.id,
|
|
)
|
|
|
|
category.update!(read_restricted: true)
|
|
|
|
expect(Site.new(guardian).categories.map { |c| c[:id] }).to contain_exactly(
|
|
SiteSetting.uncategorized_category_id,
|
|
)
|
|
end
|
|
|
|
it "includes categories that a user's group can see" do
|
|
group = Fabricate(:group)
|
|
category.update!(read_restricted: true)
|
|
category.groups << group
|
|
|
|
expect(Site.new(guardian).categories.map { |c| c[:id] }).to contain_exactly(
|
|
SiteSetting.uncategorized_category_id,
|
|
)
|
|
|
|
group.add(user)
|
|
|
|
expect(Site.new(Guardian.new(user)).categories.map { |c| c[:id] }).to contain_exactly(
|
|
SiteSetting.uncategorized_category_id,
|
|
category.id,
|
|
)
|
|
end
|
|
|
|
it "omits categories users can not write to from the category list" do
|
|
expect(Site.new(guardian).categories.count).to eq(2)
|
|
|
|
category.set_permissions(everyone: :create_post)
|
|
category.save!
|
|
|
|
guardian = Guardian.new(user)
|
|
|
|
expect(
|
|
Site.new(guardian).categories.keep_if { |c| c[:name] == category.name }.first[:permission],
|
|
).not_to eq(CategoryGroup.permission_types[:full])
|
|
|
|
# If a parent category is not visible, the child categories should not be returned
|
|
category.set_permissions(staff: :full)
|
|
category.save!
|
|
|
|
sub_category = Fabricate(:category, parent_category_id: category.id)
|
|
expect(Site.new(guardian).categories).not_to include(sub_category)
|
|
end
|
|
|
|
it "should clear the cache when custom fields are updated" do
|
|
Site.preloaded_category_custom_fields << "enable_marketplace"
|
|
categories = Site.new(Guardian.new).categories
|
|
|
|
expect(categories.last[:custom_fields]["enable_marketplace"]).to eq(nil)
|
|
|
|
category.custom_fields["enable_marketplace"] = true
|
|
category.save_custom_fields
|
|
|
|
categories = Site.new(Guardian.new).categories
|
|
|
|
expect(categories.last[:custom_fields]["enable_marketplace"]).to eq("t")
|
|
|
|
category.upsert_custom_fields(enable_marketplace: false)
|
|
|
|
categories = Site.new(Guardian.new).categories
|
|
|
|
expect(categories.last[:custom_fields]["enable_marketplace"]).to eq("f")
|
|
ensure
|
|
Site.reset_preloaded_category_custom_fields
|
|
end
|
|
|
|
it "sets the can_edit field for categories correctly" do
|
|
categories = Site.new(Guardian.new).categories
|
|
|
|
expect(categories.map { |c| c[:can_edit] }).to contain_exactly(false, false)
|
|
|
|
site = Site.new(Guardian.new(Fabricate(:moderator)))
|
|
|
|
expect(site.categories.map { |c| c[:can_edit] }).to contain_exactly(false, false)
|
|
|
|
SiteSetting.moderators_manage_categories = true
|
|
|
|
site = Site.new(Guardian.new(Fabricate(:moderator)))
|
|
|
|
expect(site.categories.map { |c| c[:can_edit] }).to contain_exactly(true, true)
|
|
end
|
|
|
|
describe "site_all_categories_cache_query modifier" do
|
|
fab!(:cool_category) { Fabricate(:category, name: "Cool category") }
|
|
fab!(:boring_category) { Fabricate(:category, name: "Boring category") }
|
|
|
|
it "allows changing the query" do
|
|
prefetched_categories = Site.new(Guardian.new(user)).categories.map { |c| c[:id] }
|
|
expect(prefetched_categories).to include(cool_category.id, boring_category.id)
|
|
|
|
# we need to clear the cache to ensure that the categories list will be updated
|
|
Site.clear_cache
|
|
|
|
plugin_instance = Plugin::Instance.new
|
|
modifier_block = Proc.new { |query| query.where("categories.name LIKE 'Cool%'") }
|
|
plugin_instance.register_modifier(:site_all_categories_cache_query, &modifier_block)
|
|
|
|
prefetched_categories = Site.new(Guardian.new(user)).categories.map { |c| c[:id] }
|
|
|
|
expect(prefetched_categories).to include(cool_category.id)
|
|
expect(prefetched_categories).not_to include(boring_category.id)
|
|
ensure
|
|
DiscoursePluginRegistry.unregister_modifier(
|
|
plugin_instance,
|
|
:site_all_categories_cache_query,
|
|
&modifier_block
|
|
)
|
|
end
|
|
end
|
|
|
|
context "with lazy loaded categories enabled" do
|
|
fab!(:user)
|
|
|
|
before { SiteSetting.lazy_load_categories_groups = "#{Group::AUTO_GROUPS[:everyone]}" }
|
|
|
|
it "does not return any categories for anonymous users" do
|
|
site = Site.new(Guardian.new)
|
|
|
|
expect(site.categories).to eq([])
|
|
end
|
|
|
|
it "returns only sidebar categories and their ancestors" do
|
|
SiteSetting.max_category_nesting = 3
|
|
grandfather_category = Fabricate(:category)
|
|
parent_category = Fabricate(:category, parent_category: grandfather_category)
|
|
category.update!(parent_category: parent_category)
|
|
Fabricate(:category_sidebar_section_link, linkable: category, user: user)
|
|
|
|
site = Site.new(Guardian.new(user))
|
|
|
|
expect(site.categories.map { |c| c[:id] }).to contain_exactly(
|
|
grandfather_category.id,
|
|
parent_category.id,
|
|
category.id,
|
|
)
|
|
end
|
|
|
|
it "returns only visible sidebar categories" do
|
|
Fabricate(:category_sidebar_section_link, linkable: category, user: user)
|
|
category.update!(read_restricted: true)
|
|
|
|
site = Site.new(Guardian.new(user))
|
|
|
|
expect(site.categories).to eq([])
|
|
end
|
|
end
|
|
end
|
|
|
|
it "omits groups user can not see" do
|
|
user = Fabricate(:user)
|
|
site = Site.new(Guardian.new(user))
|
|
|
|
staff_group = Fabricate(:group, visibility_level: Group.visibility_levels[:staff])
|
|
expect(site.groups.pluck(:name)).not_to include(staff_group.name)
|
|
|
|
public_group = Fabricate(:group)
|
|
expect(site.groups.pluck(:name)).to include(public_group.name)
|
|
|
|
admin = Fabricate(:admin)
|
|
site = Site.new(Guardian.new(admin))
|
|
expect(site.groups.pluck(:name)).to include(staff_group.name, public_group.name, "everyone")
|
|
end
|
|
|
|
describe "site_groups_query modifier" do
|
|
fab!(:user)
|
|
fab!(:cool_group) { Fabricate(:group, name: "cool-group") }
|
|
fab!(:boring_group) { Fabricate(:group, name: "boring-group") }
|
|
|
|
it "allows changing the query" do
|
|
prefetched_groups = Site.new(Guardian.new(user)).groups.map { |c| c[:id] }
|
|
expect(prefetched_groups).to include(cool_group.id, boring_group.id)
|
|
|
|
# we need to clear the cache to ensure that the groups list will be updated
|
|
Site.clear_cache
|
|
|
|
plugin_instance = Plugin::Instance.new
|
|
modifier_block = Proc.new { |query| query.where("groups.name LIKE 'cool%'") }
|
|
plugin_instance.register_modifier(:site_groups_query, &modifier_block)
|
|
|
|
prefetched_groups = Site.new(Guardian.new(user)).groups.map { |c| c[:id] }
|
|
|
|
expect(prefetched_groups).to include(cool_group.id)
|
|
expect(prefetched_groups).not_to include(boring_group.id)
|
|
ensure
|
|
DiscoursePluginRegistry.unregister_modifier(
|
|
plugin_instance,
|
|
:site_groups_query,
|
|
&modifier_block
|
|
)
|
|
end
|
|
end
|
|
|
|
it "includes all enabled authentication providers" do
|
|
SiteSetting.enable_twitter_logins = true
|
|
SiteSetting.enable_facebook_logins = true
|
|
data = JSON.parse(Site.json_for(Guardian.new))
|
|
expect(data["auth_providers"].map { |a| a["name"] }).to contain_exactly("facebook", "twitter")
|
|
end
|
|
|
|
it "includes all enabled authentication providers for anon when login_required" do
|
|
SiteSetting.login_required = true
|
|
SiteSetting.enable_twitter_logins = true
|
|
SiteSetting.enable_facebook_logins = true
|
|
data = JSON.parse(Site.json_for(Guardian.new))
|
|
expect(data["auth_providers"].map { |a| a["name"] }).to contain_exactly("facebook", "twitter")
|
|
end
|
|
|
|
it "includes tos_url and privacy_policy_url when login_required" do
|
|
SiteSetting.login_required = true
|
|
SiteSetting.tos_url = "https://discourse.org"
|
|
SiteSetting.privacy_policy_url = "https://discourse.org/privacy"
|
|
|
|
data = JSON.parse(Site.json_for(Guardian.new))
|
|
|
|
expect(data["tos_url"]).to eq(SiteSetting.tos_url)
|
|
expect(data["privacy_policy_url"]).to eq(SiteSetting.privacy_policy_url)
|
|
end
|
|
|
|
describe ".all_categories_cache" do
|
|
fab!(:category)
|
|
fab!(:category2, :category)
|
|
|
|
it "returns cached categories" do
|
|
categories_data = Site.all_categories_cache
|
|
expect(categories_data.map { |c| c[:id] }).to contain_exactly(
|
|
SiteSetting.uncategorized_category_id,
|
|
category.id,
|
|
category2.id,
|
|
)
|
|
end
|
|
|
|
it "caches the result" do
|
|
Site.all_categories_cache
|
|
|
|
category2.update_columns(name: "derp")
|
|
|
|
# The cached result should not contain
|
|
# the updated name that skipped validations
|
|
cached_names = Site.all_categories_cache.map { |c| c[:name] }
|
|
expect(cached_names).not_to include("derp")
|
|
|
|
Site.clear_cache
|
|
refreshed_names = Site.all_categories_cache.map { |c| c[:name] }
|
|
expect(refreshed_names).to include("derp")
|
|
end
|
|
|
|
it "includes preloaded custom fields" do
|
|
Site.reset_preloaded_category_custom_fields
|
|
Site.preloaded_category_custom_fields << "test_field"
|
|
|
|
category.custom_fields["test_field"] = "test_value"
|
|
category.save_custom_fields
|
|
|
|
categories_data = Site.all_categories_cache
|
|
category_data = categories_data.find { |c| c[:id] == category.id }
|
|
|
|
expect(category_data[:custom_fields]["test_field"]).to eq("test_value")
|
|
ensure
|
|
Site.reset_preloaded_category_custom_fields
|
|
end
|
|
|
|
it "applies plugin modifiers to the query" do
|
|
plugin_instance = Plugin::Instance.new
|
|
modifier_block =
|
|
Proc.new { |query| query.where("categories.name LIKE ?", "#{category.name}%") }
|
|
|
|
plugin_instance.register_modifier(:site_all_categories_cache_query, &modifier_block)
|
|
|
|
Site.clear_cache
|
|
categories_data = Site.all_categories_cache
|
|
|
|
expect(categories_data.map { |c| c[:id] }).to contain_exactly(category.id)
|
|
ensure
|
|
DiscoursePluginRegistry.unregister_modifier(
|
|
plugin_instance,
|
|
:site_all_categories_cache_query,
|
|
&modifier_block
|
|
)
|
|
end
|
|
|
|
describe "content_localization_enabled" do
|
|
it "returns localized category names when enabled" do
|
|
SiteSetting.content_localization_enabled = true
|
|
|
|
category = Fabricate(:category, locale: "en")
|
|
localization = Fabricate(:category_localization, category:)
|
|
locale = localization.locale.to_sym
|
|
|
|
I18n.locale = locale
|
|
|
|
all_categories_cache = Site.all_categories_cache
|
|
cached_category = all_categories_cache.find { |c| c[:id] == category.id }
|
|
expect(cached_category[:name]).to eq(localization.name)
|
|
expect(cached_category[:description]).to eq(localization.description)
|
|
end
|
|
|
|
it "returns original names when enabled" do
|
|
SiteSetting.content_localization_enabled = true
|
|
|
|
category = Fabricate(:category, name: "derp", description: "derp derp")
|
|
|
|
all_categories_cache = Site.all_categories_cache
|
|
cached_category = all_categories_cache.find { |c| c[:id] == category.id }
|
|
expect(cached_category[:name]).to eq(category.name)
|
|
expect(cached_category[:description]).to eq(category.description)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when there are anonymous users with different locales" do
|
|
let(:anon_guardian) { Guardian.new }
|
|
let(:original_locale) { I18n.locale }
|
|
|
|
before do
|
|
SiteSetting.login_required = false
|
|
Discourse.redis.flushdb
|
|
I18n.available_locales = %i[en ja]
|
|
I18n.locale = :en
|
|
end
|
|
|
|
after do
|
|
I18n.available_locales = nil
|
|
I18n.locale = original_locale
|
|
end
|
|
|
|
context "when content_localization_enabled is disabled" do
|
|
before { SiteSetting.content_localization_enabled = false }
|
|
|
|
it "caches anon site json with a global key (not locale scoped)" do
|
|
expect(Discourse.redis.get("site_json")).to be_nil
|
|
|
|
json = Site.json_for(anon_guardian)
|
|
|
|
expect(Discourse.redis.get("site_json")).to eq(json)
|
|
|
|
I18n.locale = :ja
|
|
json_ja = Site.json_for(anon_guardian)
|
|
expect(Discourse.redis.get("site_json")).to eq(json_ja)
|
|
|
|
# always overwritten, not per locale
|
|
I18n.locale = :en
|
|
expect(Discourse.redis.get("site_json")).to eq(json)
|
|
end
|
|
end
|
|
|
|
context "when content_localization_enabled is enabled" do
|
|
before { SiteSetting.content_localization_enabled = true }
|
|
|
|
it "caches anon site json separately for each locale" do
|
|
expect(Discourse.redis.get("site_json_en")).to be_nil
|
|
expect(Discourse.redis.get("site_json_ja")).to be_nil
|
|
|
|
json_en = Site.json_for(anon_guardian)
|
|
expect(Discourse.redis.get("site_json_en")).to eq(json_en)
|
|
|
|
I18n.locale = :ja
|
|
json_ja = Site.json_for(anon_guardian)
|
|
expect(Discourse.redis.get("site_json_ja")).to eq(json_ja)
|
|
|
|
expect(json_en).not_to eq(json_ja)
|
|
end
|
|
|
|
it "returns localized tag names for anonymous users based on locale" do
|
|
SiteSetting.tagging_enabled = true
|
|
tag = Fabricate(:tag, name: "cats", locale: "en")
|
|
Fabricate(:tag_localization, tag:, locale: "ja", name: "猫")
|
|
Fabricate(:topic, tags: [tag])
|
|
|
|
I18n.locale = :en
|
|
Discourse.redis.flushdb
|
|
json_en = Site.json_for(anon_guardian)
|
|
|
|
I18n.locale = :ja
|
|
json_ja = Site.json_for(anon_guardian)
|
|
|
|
expect(json_en).to include("cats")
|
|
expect(json_en).not_to include("猫")
|
|
expect(json_ja).to include("猫")
|
|
end
|
|
end
|
|
end
|
|
end
|