DEV: Introduce theme-owned color palettes (#30915)

Related to https://github.com/discourse/discourse/pull/30893

As part of the theme overhauling project, we're making each theme fully
own/control its color palette which can be edited directly on the theme
page. To make this possible, we need to introduce a special type of
color palettes that are marked as "owned by a theme" in the database
which aren't displayed in the admin color palettes page and can't be
edited from it. This commit is the first step of this change; it adds a new
join table to associate a color palette with a theme. For now, we're
keeping the relationship one-to-one (hence the `UNIQUE` indexes), but we
may later change it to one-to-many.

Internal topic: t/141648.
This commit is contained in:
Osama Sayegh 2025-01-22 12:03:37 +03:00 committed by GitHub
parent f7904d82b7
commit a793f4843b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 89 additions and 0 deletions

View File

@ -312,6 +312,13 @@ class ColorScheme < ActiveRecord::Base
after_destroy :dump_caches
belongs_to :theme
has_one :theme_color_scheme
has_one :owning_theme, class_name: "Theme", through: :theme_color_scheme, source: :theme
default_scope do
where("color_schemes.id NOT IN (SELECT color_scheme_id FROM theme_color_schemes)")
end
validates_associated :color_scheme_colors
BASE_COLORS_FILE = "#{Rails.root}/app/assets/stylesheets/common/foundation/colors.scss"

View File

@ -46,6 +46,12 @@ class Theme < ActiveRecord::Base
-> { where(target_id: Theme.targets[:settings], name: "yaml") },
class_name: "ThemeField"
has_one :javascript_cache, dependent: :destroy
has_one :theme_color_scheme, dependent: :destroy
has_one :owned_color_scheme,
class_name: "ColorScheme",
through: :theme_color_scheme,
source: :color_scheme
has_many :locale_fields,
-> { filter_locale_fields(I18n.fallbacks[I18n.locale]) },
class_name: "ThemeField"

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class ThemeColorScheme < ActiveRecord::Base
belongs_to :theme
belongs_to :color_scheme, dependent: :destroy
end
# == Schema Information
#
# Table name: theme_color_schemes
#
# id :bigint not null, primary key
# theme_id :integer not null
# color_scheme_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_theme_color_schemes_on_color_scheme_id (color_scheme_id) UNIQUE
# index_theme_color_schemes_on_theme_id (theme_id) UNIQUE
#

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
class CreateThemeColorScheme < ActiveRecord::Migration[7.2]
def change
create_table :theme_color_schemes do |t|
t.integer :theme_id, null: false
t.integer :color_scheme_id, null: false
t.timestamps null: false
end
add_index :theme_color_schemes, :theme_id, unique: true
add_index :theme_color_schemes, :color_scheme_id, unique: true
end
end

View File

@ -1585,4 +1585,15 @@ HTML
expect(count).to eq 3
end
end
describe "#owned_color_scheme" do
it "is destroyed when the theme is destroyed" do
scheme = Fabricate(:color_scheme, owning_theme: theme)
theme.destroy!
expect(ThemeColorScheme.exists?(color_scheme_id: scheme.id)).to eq(false)
expect(ColorScheme.unscoped.exists?(id: scheme.id)).to eq(false)
end
end
end

View File

@ -45,6 +45,18 @@ RSpec.describe Admin::ColorSchemesController do
expect(scheme_colors[0]["name"]).to eq("primary")
expect(scheme_colors[0]["hex"]).to eq(scheme.resolved_colors["primary"])
end
it "doesn't list theme-owned color schemes" do
owned_scheme = Fabricate(:color_scheme, owning_theme: Fabricate(:theme))
scheme = Fabricate(:color_scheme)
get "/admin/color_schemes.json"
expect(response.status).to eq(200)
ids = response.parsed_body.map { |obj| obj["id"] }
expect(ids).to include(scheme.id)
expect(ids).not_to include(owned_scheme.id)
end
end
shared_examples "color schemes inaccessible" do
@ -148,6 +160,15 @@ RSpec.describe Admin::ColorSchemesController do
expect(response.status).to eq(422)
expect(response.parsed_body["errors"]).to be_present
end
it "doesn't allow editing theme-owned schemes" do
color_scheme = Fabricate(:color_scheme, owning_theme: Fabricate(:theme))
put "/admin/color_schemes/#{color_scheme.id}.json", params: valid_params
expect(response.status).to eq(404)
color_scheme.reload
expect(color_scheme.name).not_to eq(valid_params[:color_scheme][:name])
end
end
shared_examples "color scheme update not allowed" do
@ -184,6 +205,14 @@ RSpec.describe Admin::ColorSchemesController do
}.by(-1)
expect(response.status).to eq(200)
end
it "doesn't allow deleting theme-owned schemes" do
color_scheme = Fabricate(:color_scheme, owning_theme: Fabricate(:theme))
delete "/admin/color_schemes/#{color_scheme.id}.json"
expect(response.status).to eq(404)
expect(color_scheme.reload).to be_persisted
end
end
shared_examples "color scheme deletion not allowed" do